From e7146beda68164a514208ec11d1e36236e0b7ef8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 18:45:33 +0000 Subject: [PATCH 01/24] Initial plan From 198fdfe4f0b0adef6ec56a7f0b19969ae8735fcd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 18:48:56 +0000 Subject: [PATCH 02/24] Initial plan for NeL 3D decal integration Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- .../leveldesign/csv_transform/sadge_lib/include/text_input.h | 2 +- .../leveldesign/csv_transform/sadge_lib/include/text_output.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_input.h b/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_input.h index 8a5283ed78..c2c32b8f27 100644 --- a/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_input.h +++ b/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_input.h @@ -17,7 +17,7 @@ //------------------------------------------------------------------------------------------------- // project: sadge lib // file: text_input.h -// version: $Id: text_input.h,v 1.2 2004/06/15 17:33:37 boucher Exp $ +// version: $Id$ // //------------------------------------------------------------------------------------------------- diff --git a/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_output.h b/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_output.h index ba77ab9e24..5d236e61e5 100644 --- a/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_output.h +++ b/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_output.h @@ -20,7 +20,7 @@ //------------------------------------------------------------------------------------------------- // project: sadge lib // file: text_output.h -// version: $Id: text_output.h,v 1.1 2004/02/10 17:54:46 boucher Exp $ +// version: $Id$ // // This file contains a string class derived from the STL string // The string compare functions od the class are case insensitive From 54c43afee74f77ff88ebd55a42ba7bad51962763 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 18:58:06 +0000 Subject: [PATCH 03/24] Add NeL 3D decal system and rename Ryzom client decal to CLegacyDecal Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/include/nel/3d/decal.h | 211 ++++++++++++++ nel/include/nel/3d/decal_manager.h | 124 ++++++++ nel/include/nel/3d/render_trav.h | 11 + nel/include/nel/3d/scene.h | 2 + nel/include/nel/3d/scene_user.h | 3 + nel/include/nel/3d/u_decal.h | 70 +++++ nel/include/nel/3d/u_scene.h | 6 + nel/include/nel/3d/visual_collision_manager.h | 8 + nel/include/nel/3d/visual_collision_mesh.h | 8 +- nel/src/3d/decal.cpp | 268 ++++++++++++++++++ nel/src/3d/decal_manager.cpp | 179 ++++++++++++ nel/src/3d/render_trav.cpp | 8 + nel/src/3d/scene.cpp | 2 + nel/src/3d/scene_user.cpp | 33 +++ nel/src/3d/visual_collision_manager.cpp | 28 ++ nel/src/3d/visual_collision_mesh.cpp | 117 ++++++++ ryzom/client/src/decal.cpp | 58 ++-- ryzom/client/src/decal.h | 20 +- ryzom/client/src/decal_anim.cpp | 6 +- ryzom/client/src/decal_anim.h | 8 +- ryzom/client/src/landscape_poly_drawer.cpp | 2 +- ryzom/client/src/main_loop.cpp | 2 +- .../r2/displayer_visual_activity_sequence.cpp | 4 +- .../r2/displayer_visual_activity_sequence.h | 2 +- ryzom/client/src/r2/editor.cpp | 6 +- ryzom/client/src/r2/editor.h | 24 +- ryzom/client/src/r2/prim_render.cpp | 6 +- ryzom/client/src/r2/prim_render.h | 6 +- ryzom/client/src/r2/tool_choose_pos.h | 4 +- ryzom/client/src/r2/tool_draw_prim.h | 2 +- 30 files changed, 1150 insertions(+), 78 deletions(-) create mode 100644 nel/include/nel/3d/decal.h create mode 100644 nel/include/nel/3d/decal_manager.h create mode 100644 nel/include/nel/3d/u_decal.h create mode 100644 nel/src/3d/decal.cpp create mode 100644 nel/src/3d/decal_manager.cpp diff --git a/nel/include/nel/3d/decal.h b/nel/include/nel/3d/decal.h new file mode 100644 index 0000000000..3d5db7215e --- /dev/null +++ b/nel/include/nel/3d/decal.h @@ -0,0 +1,211 @@ +/** \file decal.h + * + * + * $Id$ + */ + +/* Copyright, 2007 Nevrax Ltd. + * + * This file is part of NEVRAX NEL. + * NEVRAX NEL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + + * NEVRAX NEL is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with NEVRAX NEL; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef NL_DECAL_H +#define NL_DECAL_H + + +#include "nel/3d/transform.h" + +#include "nel/3d/material.h" +#include "nel/3d/shadow_map.h" +#include "nel/3d/vertex_buffer.h" +#include "nel/3d/index_buffer.h" +#include "nel/3d/u_driver.h" +#include "nel/3d/shadow_poly_receiver.h" +#include "nel/misc/polygon.h" + + +namespace NLMISC +{ + +class CPlane; + +} + + +namespace NL3D +{ + +class CScene; +class CShadowMap; +class CShadowPolyReceiver; +class CDecalManager; + +class UScene; +class UDriver; + + +// *************************************************************************** +const NLMISC::CClassId DecalId=NLMISC::CClassId(0x6a570fe6, 0x32323e16); + + +// *************************************************************************** +/** + * Decals are render on mesh + * \author Christopher Tarento + * \author Nevrax France + * \date 2007 + */ +class CDecalContext +{ +public: + CDecalContext(); + +public: + std::vector WorldClipPlanes; + CAABBox WorldBBox; + NLMISC::CPolygon2D Poly2D; + CMatrix WorldMatrix; + std::vector *DestTris; + bool Clipping; +}; + + +/** + * Decals are render on mesh + * \author Christopher Tarento + * \author Nevrax France + * \date 2007 + */ +class CDecal : public CTransform +{ +public: + /** Contructor + */ + CDecal(); + + /** Destructor + */ + ~CDecal(); + + /** Initilization + * Implementation for Ctransform + */ + void initModel(); + + /** Registering decal as valid model + * Needed for scene auto registering process + */ + static void registerBasic(); + + /** Render traversal + * No-op. Add this decal to the decal manager + */ + void traverseRender(); + + /** Get Material + * \return decal material + */ + CMaterial &getMaterial(){return _Mat;} + + /** Get Vertices + * Recompute vertices if needed and return update vector + * \return vector of vertices ready to be rendered + */ + std::vector &getVertices(const bool useVertexProgram); + + /** Set UV coordinate + * Allow decal to share a single texture + * \param v1 (0,0) corner + * \param v2 (1,1) corner + */ + void setUVCoord(const CUV uv1, const CUV uv2); + + /** Enable decal Clipping + * Rendering is direct or after processing clipping + * \param clip enable or disable clipping + */ + void enableClipping(const bool clip){_DecalContext.Clipping = clip;} + + /** Indicates if this decal is a static one + * Allow to manager specific optimizations + * \param isStatic value + */ + void setStatic(const bool isStatic){_IsStatic = isStatic;} + + +private: + /** Creator + * Used when we add a decal to the scene + */ + static CTransform *creator() {return new CDecal();} + + /** Compute decal collision and projection on shape + * Called by getVertices + * \param useVertexProgram Indicate if we must use vertex program for rendering + */ + void computeDecal(const bool useVertexProgram); + + ///////////////////////////////////////////////////////////// + +public: + /** Get the Matrix that transforms local coordinates in uv coordinates + * matrix to map (x,y) = (0,0) to (u,v) = (0,1) & (x,y) = (0,1) to (u,v) = (0,0) in local decal space + * \return reverse Matrix + */ + static CMatrix getReverseUVMatrix() + { + CMatrix m; + m.setRot(CVector::I,-CVector::J,CVector::K); + m.setPos(CVector::J); + return m; + }; + +private: + CVertexBuffer _Vb; + + CIndexBuffer _Ib; + + CMaterial _Mat; + /////////////////////////////////////////////////////////////// + bool _Touched; + + CVector _LastCamPos; + CVector _ClipCorners[4]; + CVector _RefPosition; + float _WorldToUVMatrix[4][4]; + CMatrix _InvertedWorldMatrix; + CMatrix _TextureMatrix; + float _WorldMatrixFlat[16]; + + CShadowMap *_ShadowMap; + + + CVertexBuffer _VB; + bool _VBInitialized; + std::vector _Vertices; + bool _IsStatic; + + CUV _UV1; + CUV _UV2; + CDecalContext _DecalContext; + ////////////////////////////////////////////////////////////// + + +}; + +}//NL3D +#endif diff --git a/nel/include/nel/3d/decal_manager.h b/nel/include/nel/3d/decal_manager.h new file mode 100644 index 0000000000..7a86144586 --- /dev/null +++ b/nel/include/nel/3d/decal_manager.h @@ -0,0 +1,124 @@ +/** \file decal_manager.h + * + * + * $Id$ + */ + +/* Copyright, 2007 Nevrax Ltd. + * + * This file is part of NEVRAX NEL. + * NEVRAX NEL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + + * NEVRAX NEL is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with NEVRAX NEL; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef NL_DECAL_MANAGER_H +#define NL_DECAL_MANAGER_H + +#include + +#include "nel/3d/transform.h" + +#include "nel/3d/decal.h" +#include "nel/3d/material.h" +#include "nel/3d/vertex_buffer.h" +#include "nel/3d/index_buffer.h" +#include "nel/3d/driver.h" + + + + +namespace NLMISC +{ + class CPlane; +} + + +namespace NL3D +{ + +class CScene; + + + + +// *************************************************************************** +/** + * Decal Manager contains decal and computes batch rendering + * \author Christopher Tarento + * \author Nevrax France + * \date 2007 + */ +class CDecalManager +{ + +public: + /** Constructor + */ + CDecalManager(); + + /** Destructor + */ + ~CDecalManager(); + + /** Render all decals + * \param sc Owner scene + */ + void flush(CScene *sc); + + /** Add a decal to the manager + * \param decal Decal to add to the manager + * \param texName Name of the decal's texture + */ + void addDecal(CDecal *decal, const std::string &texName); + + /** Clear all the decals of the manager + * + */ + void clearAllDecals(); + + /** Indicates if we must recompute decals position + * \param b true or false + */ + void setTouched(const bool b){_Touched = b;} + + /** Indicates if we must use vertex program for rendering + * \param b true or false + */ + void setVertexProgram(const bool b){_UseVertexProgram = b;} + +private: + ///tmp + void computeBatch(); + void renderStatic(); + void renderDynamic(); + +private: + typedef CHashMap > TDecalMap; + TDecalMap _Decals; + + bool _UseVertexProgram; + + std::vector _StaticDecals; + std::vector _DynamicDecals; + CVertexBuffer _VBRaw;//static, sorted by material + CVertexBuffer _VB;//dynamic + uint _UsedVertices; + bool _Touched;///someone has moved ? +}; + + + +}//NL3D +#endif diff --git a/nel/include/nel/3d/render_trav.h b/nel/include/nel/3d/render_trav.h index 8ea14fabff..7bf31ec2df 100644 --- a/nel/include/nel/3d/render_trav.h +++ b/nel/include/nel/3d/render_trav.h @@ -29,6 +29,7 @@ #include "nel/3d/light.h" #include "nel/3d/mesh_block_manager.h" #include "nel/3d/shadow_map_manager.h" +#include "nel/3d/decal_manager.h" #include "nel/3d/u_scene.h" #include "nel/3d/vertex_program.h" #include "nel/3d/transform.h" @@ -48,6 +49,7 @@ class CMaterial; class CTransform; class CLandscapeModel; +class CDecal; class CVertexStreamManager; class CWaterModel; @@ -247,8 +249,15 @@ class CRenderTrav : public CTravCameraScene CVertexStreamManager *getShadowMeshSkinManager() const {return _ShadowMeshSkinManager;} + /// get the decal manager + CDecalManager &getDecalManager() {return _DecalManager;} + const CDecalManager &getDecalManager() const {return _DecalManager;} + + // add a landscape. Special for CLandscapeModel::traverseRender(); void addRenderLandscape(CLandscapeModel *model); + // add a decal. Special for CDecal::traverseRender(); + void addRenderDecal(CDecal *decal); /// \name Temp Debug @@ -460,6 +469,8 @@ class CRenderTrav : public CTravCameraScene CShadowMapManager _ShadowMapManager; /// The SkinManager, but For Shadow rendering CVertexStreamManager *_ShadowMeshSkinManager; + /// The decal manager + CDecalManager _DecalManager; /** \name Special Landscape RenderList. diff --git a/nel/include/nel/3d/scene.h b/nel/include/nel/3d/scene.h index 39d9707b7d..410e24d5a4 100644 --- a/nel/include/nel/3d/scene.h +++ b/nel/include/nel/3d/scene.h @@ -69,6 +69,8 @@ class CRootModel; class CVisualCollisionManager; class CTextureCube; class CWaterEnvMap; +class CDecal; +class CDecalManager; // *************************************************************************** /** diff --git a/nel/include/nel/3d/scene_user.h b/nel/include/nel/3d/scene_user.h index 4abadb32eb..38c481ee95 100644 --- a/nel/include/nel/3d/scene_user.h +++ b/nel/include/nel/3d/scene_user.h @@ -161,6 +161,9 @@ class CSceneUser : public UScene virtual UPointLight createPointLight(); virtual void deletePointLight(UPointLight &light); + virtual UDecal createDecal(); + virtual void deleteDecal(UDecal &decal); + //@} /// \name Animation gestion. diff --git a/nel/include/nel/3d/u_decal.h b/nel/include/nel/3d/u_decal.h new file mode 100644 index 0000000000..80649f0128 --- /dev/null +++ b/nel/include/nel/3d/u_decal.h @@ -0,0 +1,70 @@ +/** \file u_decal.h + * TODO: File description + * + * $Id$ + */ + +/* Copyright, 2001 Nevrax Ltd. + * + * This file is part of NEVRAX NEL. + * NEVRAX NEL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + + * NEVRAX NEL is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with NEVRAX NEL; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef NL_U_DECAL_H +#define NL_U_DECAL_H + +#include "nel/misc/types_nl.h" +#include "nel/3d/viewport.h" +#include "nel/3d/scissor.h" +#include "nel/3d/frustum.h" +#include "nel/misc/geom_ext.h" +#include "nel/misc/matrix.h" +#include "nel/misc/rgba.h" +#include "nel/misc/rect.h" +#include "nel/misc/bitmap.h" +#include "nel/misc/event_server.h" +#include "nel/misc/event_listener.h" +#include "nel/misc/hierarchical_timer.h" +#include "nel/3d/primitive_profile.h" +#include "nel/3d/u_transform.h" + + + +namespace NL3D +{ + +class UDecal : public UTransform +{ +public: + + /// Constructors + UDecal() { _Object = NULL; } + UDecal(class CDecal *object) { _Object = (ITransformable*)object; }; + /// Attach an object to this proxy + void attach(class CDecal *object) { _Object = (ITransformable*)object; } + /// Detach the object + void detach() { _Object = NULL; } + /// Return true if the proxy is empty() (not attached) + bool empty() const {return _Object==NULL;} + /// For advanced usage, get the internal object ptr + class CDecal *getObjectPtr() const {return (CDecal*)_Object;} + +}; + + +} //NL3D + +#endif diff --git a/nel/include/nel/3d/u_scene.h b/nel/include/nel/3d/u_scene.h index 4bb831bb29..78cb2962b3 100644 --- a/nel/include/nel/3d/u_scene.h +++ b/nel/include/nel/3d/u_scene.h @@ -50,6 +50,7 @@ class UAnimationSet; class UPlayListManager; class UPointLight; class UWaterEnvMap; +class UDecal; // **************************************************************************** @@ -272,7 +273,12 @@ class UScene /// Delete a dynamic PointLight. virtual void deletePointLight(UPointLight &light)=0; + /// Create a dynamic Decal. + virtual UDecal createDecal()=0; + /// Delete a dynamic decal. + virtual void deleteDecal(UDecal &decal)=0; + //@} /// \name Animation Mgt. diff --git a/nel/include/nel/3d/visual_collision_manager.h b/nel/include/nel/3d/visual_collision_manager.h index 7b5b7e319b..8613764e00 100644 --- a/nel/include/nel/3d/visual_collision_manager.h +++ b/nel/include/nel/3d/visual_collision_manager.h @@ -35,6 +35,7 @@ class IDriver; class CShadowMap; class CShadowMapProjector; class CMaterial; +class CDecalContext; // *************************************************************************** @@ -150,6 +151,10 @@ class CVisualCollisionManager */ void removeMeshCollision(uint id); + /** Use the MeshInstance Collision to render Decal on them + * \param cdc Decal Context which contains useful informations for computing decals + */ + void receiveDecal(CDecalContext &cdc); /** Use the MeshInstance Collision to rended a ShadowMap on them. * NB: only the minimum faces touched by the shadowmap are rendered @@ -195,6 +200,9 @@ class CVisualCollisionManager /// receive a shadow map void receiveShadowMap(const CVisualCollisionMesh::CShadowContext &shadowContext); + + /// receive a decal + void receiveDecal(CDecalContext &cdc); }; // The map of Meshes Instance diff --git a/nel/include/nel/3d/visual_collision_mesh.h b/nel/include/nel/3d/visual_collision_mesh.h index d4532be757..e4937c5edd 100644 --- a/nel/include/nel/3d/visual_collision_mesh.h +++ b/nel/include/nel/3d/visual_collision_mesh.h @@ -33,6 +33,7 @@ class IDriver; class CShadowMap; class CShadowMapProjector; class CMaterial; +class CDecalContext; // *************************************************************************** @@ -60,10 +61,8 @@ class CVisualCollisionMesh : public NLMISC::CRefCount public: CShadowContext(CMaterial &mat, CIndexBuffer &ib, CShadowMapProjector &smp) : - ShadowMapProjector(smp), ShadowMaterial(mat), IndexBuffer(ib) + Driver(NULL), ShadowMap(NULL), ShadowMapProjector(smp), ShadowMaterial(mat), IndexBuffer(ib) { - Driver= NULL; - ShadowMap= NULL; } }; @@ -88,6 +87,9 @@ class CVisualCollisionMesh : public NLMISC::CRefCount /// compute the world bbox of an instance NLMISC::CAABBox computeWorldBBox(const NLMISC::CMatrix &instanceMatrix); + /// receive a decal. compute the triangles that intersect the decal + void receiveDecal(const NLMISC::CMatrix &instanceMatrix, CDecalContext &decalContext); + /// receive a shadowMap. render in driver the triangles that intersect the shadow void receiveShadowMap(const NLMISC::CMatrix &instanceMatrix, const CShadowContext &shadowContext); diff --git a/nel/src/3d/decal.cpp b/nel/src/3d/decal.cpp new file mode 100644 index 0000000000..7312b426ec --- /dev/null +++ b/nel/src/3d/decal.cpp @@ -0,0 +1,268 @@ +/** \file decal.cpp + * + * + * $Id$ + */ + +/* Copyright, 2007 Nevrax Ltd. + * + * This file is part of NEVRAX NEL. + * NEVRAX NEL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + + * NEVRAX NEL is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with NEVRAX NEL; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "std3d.h" +#include "nel/3d/decal.h" + +#include "nel/3d/shadow_map.h" +#include "nel/3d/texture_file.h" +#include "nel/3d/scene.h" +#include "nel/3d/driver.h" +#include "nel/3d/scene_user.h" +#include "nel/3d/texture_user.h" + +#include "nel/3d/dru.h" + +#include + +using namespace std; +using namespace NL3D; +using namespace NLMISC; + + + + + + +// *************************************************************************** +CDecalContext::CDecalContext() : Clipping(true), DestTris(NULL){} + + +// *************************************************************************** +CDecal::CDecal() : + _VBInitialized(false), + _Touched(true), + _UV1(CUV(0,0)), + _UV2(CUV(1,1)) +{ + //////////// + const float hg = 5.f; + + _Vertices.push_back( CVector( hg, hg, 0.f) ); + _Vertices.push_back( CVector(-hg, hg, 0.f) ); + _Vertices.push_back( CVector(-hg, -hg, 0.f) ); + _Vertices.push_back( CVector( hg, -hg, 0.f) ); + _Vertices.push_back( CVector( hg, 0.f, hg) ); + _Vertices.push_back( CVector(-hg, 0.f, hg) ); + _Vertices.push_back( CVector(-hg, 0.f, -hg) ); + _Vertices.push_back( CVector( hg, 0.f, -hg) ); + //////////// + + setOpacity(true); + setTransparency(false); + setIsRenderable(true); + setIsLightable(true); + + ///////////////////// + // vertex buffer + //////////////////// +/* _Vb.setPreferredMemory( CVertexBuffer::AGPPreferred, false ); + _Vb.setVertexFormat( CVertexBuffer::PositionFlag ); + _Vb.setNumVertices( 4 ); + + CVertexBufferReadWrite vbAccess; + _Vb.lock( vbAccess ); + vbAccess.setVertexCoord( 0, CVector( 0.f, 0.f, 0.f) ); + vbAccess.setVertexCoord( 1, CVector( 10.f, 0.f, 0.f) ); + vbAccess.setVertexCoord( 2, CVector( 10.f, 0.f, 10.f) ); + vbAccess.setVertexCoord( 3, CVector( 0.f, 0.f, 10.f) ); + +*/ + + /////////////////////// + // material + ////////////////////// + + ///set texture environment + _Mat.setShader(CMaterial::TShader::Normal);///fixed pipeline + + _Mat.texConstantColor(0, CRGBA::White); + + _Mat.texEnvOpRGB(0, CMaterial::Replace); + _Mat.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);///out=tex + _Mat.texEnvArg1RGB(0, CMaterial::Constant, CMaterial::SrcColor); + + _Mat.texEnvOpAlpha(0, CMaterial::Replace); + _Mat.texEnvArg0Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha);///out=tex + _Mat.texEnvArg1Alpha(0, CMaterial::Constant, CMaterial::SrcAlpha); + + + _Mat.setBlend(true); + _Mat.setSrcBlend(CMaterial::srcalpha); + _Mat.setDstBlend(CMaterial::invsrcalpha); + _Mat.setZWrite(false); + _Mat.setDoubleSided(true); + + CTextureFile *tex = new CTextureFile("tex_decal.tga"); + tex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapLinear); + _Mat.enableUserTexMat(0, true); + _Mat.enableUserTexMat(1, true); + + + _Mat.setTexture(0, tex); + _Mat.setUserTexMat(0, CMatrix::Identity); + + _Mat.setTexCoordGen(0, true); + _Mat.setTexCoordGenMode(0, CMaterial::TexCoordGenObjectSpace); + + _Mat.setZBias(-0.06f); + + _Mat.getTexture(0)->setWrapS(ITexture::Clamp); + _Mat.getTexture(0)->setWrapT(ITexture::Clamp); + + +} + +// *************************************************************************** +void CDecal::initModel() +{ + _LastCamPos = getOwnerScene()->getCam()->getMatrix().getPos(); + + + _ShadowMap = new CShadowMap( &(getOwnerScene()->getRenderTrav().getShadowMapManager()) ); + +} + +// *************************************************************************** +CDecal::~CDecal() +{ + delete _ShadowMap; +} + +// *************************************************************************** +void CDecal::registerBasic() +{ + CScene::registerModel( DecalId, TransformId, CDecal::creator); +} + +// *************************************************************************** +void CDecal::traverseRender() +{ + // NL_ALLOC_CONTEXT( RdrDecal ) + // No rendering Op. But delay the clip and render after landscape rendering + + + ///TODO christo pas beau le "Jean" + static std::string texName = "Jean"; + getOwnerScene()->getRenderTrav().getDecalManager().addDecal(this, texName); + +} + +// *************************************************************************** +std::vector &CDecal::getVertices(const bool useVertexProgram) +{ + ///position of decal has changed ?? + const NLMISC::CVector &camPos = getOwnerScene()->getCam()->getMatrix().getPos(); + if ( (camPos - _LastCamPos).norm() >= 4.f ) + { + _Touched = true; + } + + if( _Touched ) ///is NOT touched, direct render + { + _LastCamPos = camPos; + computeDecal(useVertexProgram); + } + + return _Vertices; +} + +// *************************************************************************** +void CDecal::computeDecal(const bool useVertexProgram) +{ + CScene *sc = getOwnerScene(); + + /// Verify that coll manager exists + CVisualCollisionManager *vcm; + if( !(vcm=sc->getVisualCollisionManagerForShadow()) ) + nlassert("VisualCollisionManager does NOT exist, NO decal rendering"); + + + ///Preparing shadow map clipping planes + //CShadowMap sm( &(sc->getRenderTrav().getShadowMapManager()) ); + CDecalContext context; + + float decalSize = 1.0f; + _ClipCorners[0] = getWorldMatrix() * (CVector(0.f, 1.f, 0.f) * decalSize); + _ClipCorners[1] = getWorldMatrix() * (CVector(1.f, 1.f, 0.f) * decalSize); + _ClipCorners[2] = getWorldMatrix() * (CVector(1.f, 0.f, 0.f) * decalSize); + _ClipCorners[3] = getWorldMatrix() * (CVector(0.f, 0.f, 0.f) * decalSize); + + ///Unit Cube for clipping + context.WorldClipPlanes.resize(4); + context.WorldBBox.setMinMax( getWorldMatrix() * (CVector(0.f,0.f,0.f) * decalSize), getWorldMatrix() * (CVector(1.f,1.f,1.f) * decalSize) ); + + for(uint i=0; i<4; ++i) + { + context.WorldClipPlanes[i].make( + _ClipCorners[i], + _ClipCorners[(i + 1) & 3], + _ClipCorners[i] + (_ClipCorners[(i + 1) & 3] - _ClipCorners[i]).norm() * CVector::K);///&3 = %4 = %2^(3-1) + + context.WorldClipPlanes[i].invert(); + } + + + CMatrix refPosMatrix; + refPosMatrix.identity(); + refPosMatrix.setPos( sc->getCam()->getMatrix().getPos() ); + + CMatrix worldToUVMatrix = getReverseUVMatrix() * getWorldMatrix().inverted(); + //worldToUVMatrix = worldToUVMatrix * refPosMatrix; + + + //CMatrix world = getWorldMatrix(); + //smp.setWorldSpaceTextMat(CMatrix::Identity); + //_Mat.setUserTexMat(0, CMatrix::Identity); + + /*CShadowMapProjector smp; + vcm->receiveShadowMap(sc->getDriver(), &sm, CVector(0,0,0), _Mat, smp); + */ + + context.WorldMatrix = getWorldMatrix(); + + _Vertices.clear(); + context.DestTris = &_Vertices; + vcm->receiveDecal(context); + + + + sc->getDriver()->setupModelMatrix(getWorldMatrix()); + + CDRU::drawWiredBox( (CVector::K * (-10.f)), + CVector::I * decalSize, + CVector::J * decalSize, + (CVector::K * 20.f), + CRGBA::White, + *(sc->getDriver()) ); + +} + +// *************************************************************************** +void CDecal::setUVCoord(const CUV uv1, const CUV uv2) +{ + _UV1 = uv1; + _UV2 = uv2; +} diff --git a/nel/src/3d/decal_manager.cpp b/nel/src/3d/decal_manager.cpp new file mode 100644 index 0000000000..5a964ab000 --- /dev/null +++ b/nel/src/3d/decal_manager.cpp @@ -0,0 +1,179 @@ +/** \file decal_manager.cpp + * + * + * $Id$ + */ + +/* Copyright, 2007 Nevrax Ltd. + * + * This file is part of NEVRAX NEL. + * NEVRAX NEL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + + * NEVRAX NEL is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with NEVRAX NEL; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "std3d.h" +#include "nel/3d/decal_manager.h" +#include "nel/3d/scene.h" + +#include + + +using namespace std; +using namespace NLMISC; +using namespace NL3D; + + +// *************************************************************************** +CDecalManager::CDecalManager() : + _Touched(true), + _UsedVertices(0) +{ + _VB.setPreferredMemory( CVertexBuffer::AGPVolatile, true ); + _VB.setVertexFormat( CVertexBuffer::PositionFlag ); +} + +// *************************************************************************** +CDecalManager::~CDecalManager() +{ + +} + +// *************************************************************************** +void CDecalManager::clearAllDecals() +{ + _Decals.clear(); + _UsedVertices = 0; + _Touched = true; +} + +// *************************************************************************** +void CDecalManager::addDecal(CDecal *decal, const string &texName) +{ + TDecalMap::iterator itEnd = _Decals.begin(); + TDecalMap::iterator it = _Decals.find(texName); + if (it!=itEnd)///this key exist, add decal to vector + { + it->second.push_back(decal); + } + else///Not exist, create new vector + { + vector &vec = _Decals[texName]; + vec.push_back(decal); + } + + ///get number of vertices and compute prerender + //_UsedVertices += decal->getVertices(_UseVertexProgram).size(); + decal->getVertices(_UseVertexProgram); + + _Touched = true; +} + +// *************************************************************************** +void CDecalManager::computeBatch() +{ + //uint vertSize = _VB.getVertexSize(); + //_VB.setNumVertices( _UsedVertices );//tmp + + //CVertexBufferReadWrite vba; + //_VB.lock(vba); + + //static const size = sizeof(CVector); + + TDecalMap::iterator it = _Decals.begin(); + TDecalMap::iterator itEnd = _Decals.end(); + + //uint count=0; + vector accum; + for(;it!=itEnd;++it) + { + vector::iterator d = it->second.begin(); + vector::iterator dEnd = it->second.end(); + for(;d!=dEnd;++d) + { + vector &vec = (*d)->getVertices(_UseVertexProgram); + //static const uint length = vec.size(); + //if (length == 0) + // continue; + if(vec.size() == 0) + continue; + + + vector::iterator v = vec.begin(); + vector::iterator vEnd = vec.end(); + for(; v!=vEnd; ++v) + { + accum.push_back( *v ); + } + //memcpy( vba.getVertexCoordPointer(count), &vec[0], length*size ); + //count += length; + } + } + + //_VB.setNumVertices( count ); + _VB.setNumVertices( (uint32)accum.size() ); + if (accum.size()) + { + CVertexBufferReadWrite vba; + _VB.lock(vba); + + memcpy(vba.getVertexCoordPointer(0), &accum[0], accum.size() * sizeof(CVector)); + + // nlassert( count == _UsedVertices ); + nlassert(accum.size() % 3 == 0); + + vba.unlock(); + } + + _Touched = false; +} + +// *************************************************************************** +///main call for rendering +void CDecalManager::flush(CScene *sc) +{ + if (_Touched) + { + computeBatch(); + } + + IDriver *drv= sc->getRenderTrav().getDriver(); + drv->activeVertexProgram(NULL);///tmp + + drv->activeVertexBuffer(_VB); + + CMaterial mat;//tmp + mat.initUnlit(); + mat.setDoubleSided(true); + mat.setZBias(-50.0f); + + vector jean = _Decals["Jean"];//tmp + + + drv->setupModelMatrix(CMatrix::Identity);///->OK + drv->renderRawTriangles( mat, 0, _VB.getNumVertices()/3 ); + +} + +// *************************************************************************** +void CDecalManager::renderStatic() +{ + +} + +// *************************************************************************** +void CDecalManager::renderDynamic() +{ + +} diff --git a/nel/src/3d/render_trav.cpp b/nel/src/3d/render_trav.cpp index a1e1b69be6..c30c107482 100644 --- a/nel/src/3d/render_trav.cpp +++ b/nel/src/3d/render_trav.cpp @@ -226,6 +226,10 @@ void CRenderTrav::traverse(UScene::TRenderPart renderPart, bool newRender, bool // Clear any landscape clearRenderLandscapeList(); + // Clear any decal + _DecalManager.clearAllDecals(); + + // Start LodCharacter Manager render. CLodCharacterManager *clodMngr= Scene->getLodCharacterManager(); if(clodMngr) @@ -303,6 +307,10 @@ void CRenderTrav::traverse(UScene::TRenderPart renderPart, bool newRender, bool Scene->getLandscapePolyDrawingCallback()->endPolyDrawing(); } + // Render the decals + _DecalManager.flush(Scene); + + // Profile this frame? if(Scene->isNextRenderProfile()) { diff --git a/nel/src/3d/scene.cpp b/nel/src/3d/scene.cpp index 651837e2cb..14402ed6da 100644 --- a/nel/src/3d/scene.cpp +++ b/nel/src/3d/scene.cpp @@ -55,6 +55,7 @@ #include "nel/3d/async_texture_manager.h" #include "nel/3d/water_env_map.h" #include "nel/3d/skeleton_spawn_script.h" +#include "nel/3d/decal.h" #include @@ -121,6 +122,7 @@ void CScene::registerBasics() CPointLightModel::registerBasic(); CSegRemanence::registerBasic(); CQuadGridClipManager::registerBasic(); + CDecal::registerBasic(); } diff --git a/nel/src/3d/scene_user.cpp b/nel/src/3d/scene_user.cpp index 4eed212a46..4930ce9a28 100644 --- a/nel/src/3d/scene_user.cpp +++ b/nel/src/3d/scene_user.cpp @@ -23,8 +23,10 @@ #include "nel/3d/u_instance.h" #include "nel/3d/u_camera.h" #include "nel/3d/u_skeleton.h" +#include "nel/3d/u_decal.h" #include "nel/3d/scene_user.h" #include "nel/3d/skeleton_model.h" +#include "nel/3d/decal.h" #include "nel/3d/coarse_mesh_manager.h" #include "nel/3d/point_light_model.h" #include "nel/3d/lod_character_manager.h" @@ -48,6 +50,7 @@ H_AUTO_DECL( NL3D_UI_Scene ) H_AUTO_DECL( NL3D_Misc_Scene_CreateDel_Element ) H_AUTO_DECL( NL3D_CreateOrLoad_Instance ) H_AUTO_DECL( NL3D_CreateOrLoad_Skeleton ) +H_AUTO_DECL( NL3D_CreateOrLoad_Decal ) H_AUTO_DECL( NL3D_Load_CLodOrCoarseMesh ) H_AUTO_DECL( NL3D_Load_AsyncIG ) @@ -55,6 +58,7 @@ H_AUTO_DECL( NL3D_Load_AsyncIG ) #define NL3D_HAUTO_ELT_SCENE H_AUTO_USE( NL3D_Misc_Scene_CreateDel_Element ) #define NL3D_HAUTO_CREATE_INSTANCE H_AUTO_USE( NL3D_CreateOrLoad_Instance ) #define NL3D_HAUTO_CREATE_SKELETON H_AUTO_USE( NL3D_CreateOrLoad_Skeleton ) +#define NL3D_HAUTO_CREATE_DECAL H_AUTO_USE( NL3D_CreateOrLoad_Decal ) #define NL3D_HAUTO_LOAD_LOD H_AUTO_USE( NL3D_Load_CLodOrCoarseMesh ) #define NL3D_HAUTO_ASYNC_IG H_AUTO_USE( NL3D_Load_AsyncIG ) @@ -331,6 +335,35 @@ void CSceneUser::deletePointLight(UPointLight &light) light.detach (); } +// *************************************************************************** + +UDecal CSceneUser::createDecal() +{ + NL3D_HAUTO_CREATE_DECAL; + + CTransform *t = _Scene.createModel(DecalId); + if (t) + { + CDecal *decal= safe_cast (t); + return UDecal(decal); + } + else + { + return UDecal(); + } +} + +// *************************************************************************** + +void CSceneUser::deleteDecal(UDecal &decal) +{ + NL3D_HAUTO_ELT_SCENE; + + // The component is auto added/deleted to _Scene in ctor/dtor. + _Scene.deleteModel(decal.getObjectPtr()); + decal.detach (); +} + // *************************************************************************** void CSceneUser::setGlobalWindPower(float gwp) diff --git a/nel/src/3d/visual_collision_manager.cpp b/nel/src/3d/visual_collision_manager.cpp index 6d914464fc..74c13aa4e6 100644 --- a/nel/src/3d/visual_collision_manager.cpp +++ b/nel/src/3d/visual_collision_manager.cpp @@ -22,6 +22,7 @@ #include "nel/3d/camera_col.h" #include "nel/3d/shadow_map.h" #include "nel/3d/light.h" +#include "nel/3d/decal.h" #include "nel/misc/common.h" @@ -358,6 +359,33 @@ void CVisualCollisionManager::CMeshInstanceCol::receiveShadowMap(const CVisualC } } + +// *************************************************************************** +void CVisualCollisionManager::receiveDecal(CDecalContext &cdc) +{ + _MeshQuadGrid.select(cdc.WorldBBox.getMin(), cdc.WorldBBox.getMax()); + + CQuadGrid::CIterator it = _MeshQuadGrid.begin(); + CQuadGrid::CIterator itEnd = _MeshQuadGrid.end(); + + for(;it!=itEnd;++it) + { + (*it)->receiveDecal(cdc); + } + +} + +// *************************************************************************** +void CVisualCollisionManager::CMeshInstanceCol::receiveDecal(CDecalContext &cdc) +{ + // if mesh still present (else it s may be an error....) + if(Mesh) + { + // get the collision with the mesh + Mesh->receiveDecal(WorldMatrix, cdc); + } +} + // *************************************************************************** void CVisualCollisionManager::getMeshs(const NLMISC::CAABBox &aabbox, std::vector &dest) { diff --git a/nel/src/3d/visual_collision_mesh.cpp b/nel/src/3d/visual_collision_mesh.cpp index d3bd95b93b..9549d65054 100644 --- a/nel/src/3d/visual_collision_mesh.cpp +++ b/nel/src/3d/visual_collision_mesh.cpp @@ -24,6 +24,7 @@ #include "nel/3d/camera_col.h" #include "nel/3d/driver.h" #include "nel/3d/shadow_map.h" +#include "nel/3d/decal.h" using namespace std; @@ -361,6 +362,122 @@ NLMISC::CAABBox CVisualCollisionMesh::computeWorldBBox(const CMatrix &instance return ret; } +// *************************************************************************** +void CVisualCollisionMesh::receiveDecal(const NLMISC::CMatrix &instanceMatrix, CDecalContext &decalContext) +{ + nlassert( decalContext.DestTris ); + // empty mesh => no op + if(_Vertices.empty()) + return; + + // The VertexBuffer RefPtr has been released? quit + if(_VertexBuffer == NULL) + return; + + + // **** Select triangles to be rendered with quadGrid + // select with quadGrid local in mesh + CAABBox localBBox; + localBBox = CAABBox::transformAABBox(decalContext.WorldMatrix.inverted(), decalContext.WorldBBox); + + static std::vector triInQuadGrid; + triInQuadGrid.clear(); + uint numTrisInQuadGrid= _QuadGrid.select(localBBox, triInQuadGrid); + + // no intersection at all? quit + if(numTrisInQuadGrid==0) + return; + + + static std::vector localClipPlanes; + // Allow max bits of planes clip. + localClipPlanes.resize(min((uint)decalContext.WorldClipPlanes.size(), (uint)NL3D_VCM_SHADOW_NUM_CLIP_PLANE)); + // Transform into Mesh local space + for(uint i=0;i vertexFlags; + if(vertexFlags.size()<_Vertices.size()) + vertexFlags.resize(_Vertices.size()); + memset(&vertexFlags[0], 0, _Vertices.size()*sizeof(uint8)); + + + for(uint triq=0; triq 0; + + vf |= ((uint)out)<= 3) + { + for(uint k=0; kpush_back( clippedTri.Vertices[0] ); + decalContext.DestTris->push_back( clippedTri.Vertices[k + 1] ); + decalContext.DestTris->push_back( clippedTri.Vertices[k + 2] ); + } + } + + } + else + { + decalContext.DestTris->push_back( instanceMatrix * _Vertices[triId[0]] ); + decalContext.DestTris->push_back( instanceMatrix * _Vertices[triId[1]] ); + decalContext.DestTris->push_back( instanceMatrix * _Vertices[triId[2]] ); + } + + } + + } + +} // *************************************************************************** void CVisualCollisionMesh::receiveShadowMap(const NLMISC::CMatrix &instanceMatrix, const CShadowContext &shadowContext) diff --git a/ryzom/client/src/decal.cpp b/ryzom/client/src/decal.cpp index 24e3e756b9..6044f8fd43 100644 --- a/ryzom/client/src/decal.cpp +++ b/ryzom/client/src/decal.cpp @@ -49,12 +49,12 @@ using namespace NLMISC; #define new DEBUG_NEW #endif -CDecalRenderList DecalRenderList; +CLegacyDecalRenderList DecalRenderList; extern uint SkipFrame; -NL3D::CVertexBuffer CDecal::_VB; -bool CDecal::_VBInitialized = false; +NL3D::CVertexBuffer CLegacyDecal::_VB; +bool CLegacyDecal::_VBInitialized = false; @@ -156,7 +156,7 @@ static NLMISC::CSmartPtr DecalAttenuationVertexP typedef CShadowPolyReceiver::CRGBAVertex CRGBAVertex; // **************************************************************************** -CDecal::CDecal() +CLegacyDecal::CLegacyDecal() { if (!DecalAttenuationVertexProgram) { @@ -207,7 +207,7 @@ CDecal::CDecal() } // **************************************************************************** -void CDecal::setCustomUVMatrix(bool on, const NLMISC::CMatrix &matrix) +void CLegacyDecal::setCustomUVMatrix(bool on, const NLMISC::CMatrix &matrix) { if (_CustomUVMatrix.set(on, matrix)) { @@ -216,7 +216,7 @@ void CDecal::setCustomUVMatrix(bool on, const NLMISC::CMatrix &matrix) } // **************************************************************************** -const std::string &CDecal::getTextureFileName() const +const std::string &CLegacyDecal::getTextureFileName() const { CTextureFile *tf = dynamic_cast(_Material.getTexture(0)); if (tf) return tf->getFileName(); @@ -225,33 +225,33 @@ const std::string &CDecal::getTextureFileName() const } // **************************************************************************** -void CDecal::setupMaterialColor() +void CLegacyDecal::setupMaterialColor() { _Material.texConstantColor(1, NLMISC::CRGBA(_Emissive.R, _Emissive.G, _Emissive.B, _Diffuse.A)); } // **************************************************************************** -void CDecal::setEmissive(NLMISC::CRGBA emissive) +void CLegacyDecal::setEmissive(NLMISC::CRGBA emissive) { _Emissive = emissive; setupMaterialColor(); } // **************************************************************************** -void CDecal::setDiffuse(NLMISC::CRGBA diffuse) +void CLegacyDecal::setDiffuse(NLMISC::CRGBA diffuse) { _Diffuse = diffuse; setupMaterialColor(); } // **************************************************************************** -CRGBA CDecal::getDiffuse() const +CRGBA CLegacyDecal::getDiffuse() const { return _Diffuse; } // **************************************************************************** -CDecal::~CDecal() +CLegacyDecal::~CLegacyDecal() { if (_ShadowMap) { @@ -261,7 +261,7 @@ CDecal::~CDecal() } // **************************************************************************** -void CDecal::setTexture(const std::string &fileName, bool clampU, bool clampV, bool filtered) +void CLegacyDecal::setTexture(const std::string &fileName, bool clampU, bool clampV, bool filtered) { if (getTextureFileName() != fileName) { @@ -302,7 +302,7 @@ void CDecal::setTexture(const std::string &fileName, bool clampU, bool clamp } // **************************************************************************** -void CDecal::setWorldMatrix(const NLMISC::CMatrix &matrix) +void CLegacyDecal::setWorldMatrix(const NLMISC::CMatrix &matrix) { float newMat[16]; matrix.get(newMat); @@ -330,7 +330,7 @@ void CDecal::setWorldMatrix(const NLMISC::CMatrix &matrix) } // **************************************************************************** -bool CDecal::clipFront(const NLMISC::CPlane &p) const +bool CLegacyDecal::clipFront(const NLMISC::CPlane &p) const { for(uint k = 0; k < 8; ++k) { @@ -340,7 +340,7 @@ bool CDecal::clipFront(const NLMISC::CPlane &p) const } // **************************************************************************** -void CDecal::setWorldMatrixForArrow(const NLMISC::CVector2f &start, const NLMISC::CVector2f &end, float halfWidth) +void CLegacyDecal::setWorldMatrixForArrow(const NLMISC::CVector2f &start, const NLMISC::CVector2f &end, float halfWidth) { CMatrix matrix; CVector I = CVector(end.x, end.y, 0.f) - CVector(start.x, start.y, 0.f); @@ -351,7 +351,7 @@ void CDecal::setWorldMatrixForArrow(const NLMISC::CVector2f &start, const NLM } // **************************************************************************** -void CDecal::setWorldMatrixForSpot(const NLMISC::CVector2f &pos, float radius, float angleInRadians) +void CLegacyDecal::setWorldMatrixForSpot(const NLMISC::CVector2f &pos, float radius, float angleInRadians) { CMatrix matrix; matrix.rotateZ(angleInRadians); @@ -365,7 +365,7 @@ NLMISC::CVector r2MaskOffset(1.f / 4.f, 1.f / 4.f, 0.f); // **************************************************************************** -void CDecal::renderTriCache(NL3D::IDriver &drv, NL3D::CShadowPolyReceiver &/* receiver */, bool useVertexProgram) +void CLegacyDecal::renderTriCache(NL3D::IDriver &drv, NL3D::CShadowPolyReceiver &/* receiver */, bool useVertexProgram) { if (_TriCache.empty()) return; if (!_VBInitialized) @@ -441,8 +441,8 @@ void CDecal::renderTriCache(NL3D::IDriver &drv, NL3D::CShadowPolyReceiver &/* const CRGBAVertex *destEnd = dest + _TriCache.size(); const CRGBAVertex *srcVert = &_TriCache[0]; const NLMISC::CVector camPos = MainCam.getMatrix().getPos() - _RefPosition; - float scale = 255.f * CDecalRenderList::getInstance()._DistScale; - float bias = 255.f * CDecalRenderList::getInstance()._DistBias; + float scale = 255.f * CLegacyDecalRenderList::getInstance()._DistScale; + float bias = 255.f * CLegacyDecalRenderList::getInstance()._DistBias; float bottomBlendScale = 1.f / favoid0(_BottomBlendZMax - _BottomBlendZMin); float bottomBlendBias = bottomBlendScale * (_RefPosition.z - _BottomBlendZMin); do @@ -478,7 +478,7 @@ void CDecal::renderTriCache(NL3D::IDriver &drv, NL3D::CShadowPolyReceiver &/* } // **************************************************************************** -void CDecal::render(NL3D::UDriver &/* drv */, +void CLegacyDecal::render(NL3D::UDriver &/* drv */, NL3D::CShadowPolyReceiver &receiver, const std::vector &worldPyramid, const std::vector &pyramidCorners, @@ -617,7 +617,7 @@ void CDecal::render(NL3D::UDriver &/* drv */, } // **************************************************************************** -void CDecalRenderList::renderAllDecals() +void CLegacyDecalRenderList::renderAllDecals() { if (_Empty) return; @@ -655,7 +655,7 @@ void CDecalRenderList::renderAllDecals() } for(uint k = 0; k < DECAL_NUM_PRIORITIES; ++k) { - std::vector &renderList = _RenderList[k]; + std::vector &renderList = _RenderList[k]; for(uint l = 0; l < renderList.size(); ++l) { if (renderList[l]) @@ -671,7 +671,7 @@ void CDecalRenderList::renderAllDecals() } // **************************************************************************** -void CDecalRenderList::clearRenderList() +void CLegacyDecalRenderList::clearRenderList() { for(uint k = 0; k < DECAL_NUM_PRIORITIES; ++k) { @@ -681,27 +681,27 @@ void CDecalRenderList::clearRenderList() } // **************************************************************************** -void CDecal::addToRenderList(uint priority /*=0*/) +void CLegacyDecal::addToRenderList(uint priority /*=0*/) { if( !Landscape) { return; } nlassert(priority < DECAL_NUM_PRIORITIES); - CDecalRenderList &drl = CDecalRenderList::getInstance(); + CLegacyDecalRenderList &drl = CLegacyDecalRenderList::getInstance(); drl._RenderList[priority].push_back(this); drl._Empty = false; } // **************************************************************************** -bool CDecal::contains(const NLMISC::CVector2f &pos) const +bool CLegacyDecal::contains(const NLMISC::CVector2f &pos) const { CVector posIn = _InvertedWorldMatrix * CVector(pos.x, pos.y, 0.f); return posIn.x >= 0.f && posIn.x <= 1.f && posIn.y >= 0.f && posIn.y <= 1.f; } // **************************************************************************** -void CDecal::setClipDownFacing(bool clipDownFacing) +void CLegacyDecal::setClipDownFacing(bool clipDownFacing) { if (clipDownFacing != _ClipDownFacing) { @@ -711,7 +711,7 @@ void CDecal::setClipDownFacing(bool clipDownFacing) } // **************************************************************************** -void CDecal::setBottomBlend(float zMin, float zMax) +void CLegacyDecal::setBottomBlend(float zMin, float zMax) { if (zMin > zMax) std::swap(zMin, zMax); _BottomBlendZMin = zMin; @@ -719,7 +719,7 @@ void CDecal::setBottomBlend(float zMin, float zMax) } // **************************************************************************** -void CDecal::setTopBlend(float zMin, float zMax) +void CLegacyDecal::setTopBlend(float zMin, float zMax) { if (zMin > zMax) std::swap(zMin, zMax); _TopBlendZMin = zMin; diff --git a/ryzom/client/src/decal.h b/ryzom/client/src/decal.h index 1c473e74f2..0ca1dee564 100644 --- a/ryzom/client/src/decal.h +++ b/ryzom/client/src/decal.h @@ -50,13 +50,13 @@ const uint DECAL_NUM_PRIORITIES = 8; // Helper class to display a decal on a poly receiver // Default decal is a unit rectangle (0, 0) - (1, 1) // TODO nico : put this in NL3D when working ? ... -class CDecal : public NLMISC::CRefCount +class CLegacyDecal : public NLMISC::CRefCount { public: - typedef NLMISC::CRefPtr TRefPtr; - typedef NLMISC::CSmartPtr TSmartPtr; - CDecal(); - ~CDecal(); + typedef NLMISC::CRefPtr TRefPtr; + typedef NLMISC::CSmartPtr TSmartPtr; + CLegacyDecal(); + ~CLegacyDecal(); // Set a texture from its filename. It name match a global texture in the view renderer, it will be used ,first void setTexture(const std::string &fileName, bool clampU = true, bool clampV = true, bool filtered = true); NL3D::ITexture *getTexture() { return _Material.getTexture(0); } @@ -108,7 +108,7 @@ class CDecal : public NLMISC::CRefCount float _TopBlendZMin; float _TopBlendZMax; private: - friend class CDecalRenderList; + friend class CLegacyDecalRenderList; void render(NL3D::UDriver &drv, NL3D::CShadowPolyReceiver &receiver, const std::vector &worldPyramid, @@ -121,15 +121,15 @@ class CDecal : public NLMISC::CRefCount }; // list of all decals to be rendered after the landscape -class CDecalRenderList : public NLMISC::CSingleton +class CLegacyDecalRenderList : public NLMISC::CSingleton { public: - CDecalRenderList() : _Empty(true) {} + CLegacyDecalRenderList() : _Empty(true) {} void renderAllDecals(); void clearRenderList(); private: - friend class CDecal; - std::vector _RenderList[DECAL_NUM_PRIORITIES]; + friend class CLegacyDecal; + std::vector _RenderList[DECAL_NUM_PRIORITIES]; bool _Empty; std::vector _WorldCamPyramid; std::vector _WorldCamPyramidCorners; diff --git a/ryzom/client/src/decal_anim.cpp b/ryzom/client/src/decal_anim.cpp index c815961ad9..21c8a46b69 100644 --- a/ryzom/client/src/decal_anim.cpp +++ b/ryzom/client/src/decal_anim.cpp @@ -31,7 +31,7 @@ using namespace NLMISC; // ***************************************************************************** -CDecalAnim::CDecalAnim() +CLegacyDecalAnim::CLegacyDecalAnim() { DurationInMs = 1000; EndScaleFactor = 1.f; @@ -43,7 +43,7 @@ CDecalAnim::CDecalAnim() } // ***************************************************************************** -void CDecalAnim::updateDecal(const NLMISC::CVector2f &pos, float animRatio, CDecal &dest, float refScale) const +void CLegacyDecalAnim::updateDecal(const NLMISC::CVector2f &pos, float animRatio, CLegacyDecal &dest, float refScale) const { dest.setTexture(Texture); dest.setWorldMatrixForSpot(pos, refScale * blend(1.f, EndScaleFactor, animRatio), blend(0.f, EndAngleInDegrees, animRatio)); @@ -55,7 +55,7 @@ void CDecalAnim::updateDecal(const NLMISC::CVector2f &pos, float animRatio, CDec // ***************************************************************************** -void CDecalAnim::buildFromLuaTable(CLuaObject &table) +void CLegacyDecalAnim::buildFromLuaTable(CLuaObject &table) { // retrieve a value from a lua table or affect a default value if not found #define GET_LUA_VALUE(dest, Type, CastType, Default) \ diff --git a/ryzom/client/src/decal_anim.h b/ryzom/client/src/decal_anim.h index 356e405af0..9e8760ab1a 100644 --- a/ryzom/client/src/decal_anim.h +++ b/ryzom/client/src/decal_anim.h @@ -33,10 +33,10 @@ namespace NLGUI using namespace NLGUI; -class CDecal; +class CLegacyDecal; // TODO nico : this would fit nicely in the particle system animation system (would be more flexible) -class CDecalAnim +class CLegacyDecalAnim { public: std::string Texture; @@ -49,8 +49,8 @@ class CDecalAnim NLMISC::CRGBA StartEmissive; NLMISC::CRGBA EndEmissive; public: - CDecalAnim(); - void updateDecal(const NLMISC::CVector2f &pos, float animRatio, CDecal &dest, float refScale) const; + CLegacyDecalAnim(); + void updateDecal(const NLMISC::CVector2f &pos, float animRatio, CLegacyDecal &dest, float refScale) const; void buildFromLuaTable(CLuaObject &table); }; diff --git a/ryzom/client/src/landscape_poly_drawer.cpp b/ryzom/client/src/landscape_poly_drawer.cpp index 714684e263..67ca596795 100644 --- a/ryzom/client/src/landscape_poly_drawer.cpp +++ b/ryzom/client/src/landscape_poly_drawer.cpp @@ -310,7 +310,7 @@ void CLandscapePolyDrawer::renderLandscapePolyPart() Driver->stencilFunc(UDriver::equal, 0x80, 0x80); // call to render the decals just before the projected polygons on landscape - CDecalRenderList::getInstance().renderAllDecals(); + CLegacyDecalRenderList::getInstance().renderAllDecals(); // disable stencil test Driver->enableStencilTest(false); diff --git a/ryzom/client/src/main_loop.cpp b/ryzom/client/src/main_loop.cpp index 8e24ad42ff..176485edf1 100644 --- a/ryzom/client/src/main_loop.cpp +++ b/ryzom/client/src/main_loop.cpp @@ -1253,7 +1253,7 @@ bool mainLoop() CInputHandlerManager::getInstance()->pumpEvents(); CLandscapePolyDrawer::getInstance().deletePolygons(); - CDecalRenderList::getInstance().clearRenderList(); + CLegacyDecalRenderList::getInstance().clearRenderList(); // Update the Interface Manager Events. diff --git a/ryzom/client/src/r2/displayer_visual_activity_sequence.cpp b/ryzom/client/src/r2/displayer_visual_activity_sequence.cpp index 1990185c24..d1915e9d9b 100644 --- a/ryzom/client/src/r2/displayer_visual_activity_sequence.cpp +++ b/ryzom/client/src/r2/displayer_visual_activity_sequence.cpp @@ -252,7 +252,7 @@ void CDisplayerVisualActivitySequence::update() void CDisplayerVisualActivitySequence::addFootSteps(const NLMISC::CLine &line) { //H_AUTO(R2_CDisplayerVisualActivitySequence_addFootSteps) - CDecal *decal = new CDecal; + CLegacyDecal *decal = new CLegacyDecal; decal->setTexture(CV_FootStepDecalTexture.get(), false, true); CVector2f start2f(line.V0.x, line.V0.y); CVector2f end2f(line.V1.x, line.V1.y); @@ -272,7 +272,7 @@ void CDisplayerVisualActivitySequence::addFootSteps(const NLMISC::CLine &line) void CDisplayerVisualActivitySequence::addWanderSteps(const CVector &pos) { //H_AUTO(R2_CDisplayerVisualActivitySequence_addWanderSteps) - CDecal *decal = new CDecal; + CLegacyDecal *decal = new CLegacyDecal; decal->setTexture(CV_WanderDecalTexture.get(), true, true); decal->setWorldMatrixForSpot(CVector2f(pos.x, pos.y), CV_WanderDecalSize.get()); _Decals.push_back(decal); diff --git a/ryzom/client/src/r2/displayer_visual_activity_sequence.h b/ryzom/client/src/r2/displayer_visual_activity_sequence.h index 290c623098..fe7d490159 100644 --- a/ryzom/client/src/r2/displayer_visual_activity_sequence.h +++ b/ryzom/client/src/r2/displayer_visual_activity_sequence.h @@ -52,7 +52,7 @@ class CDisplayerVisualActivitySequence : public CDisplayerVisual, public CEditor bool _AddedToWorldMap; bool _Touched; bool _Active; - std::vector _Decals; + std::vector _Decals; std::vector _WorldMapEdges; std::vector _ObserverHandles; // need to know when one of the component world pos is changed (no notification message reach us if diff --git a/ryzom/client/src/r2/editor.cpp b/ryzom/client/src/r2/editor.cpp index 31e83af21d..954b995154 100644 --- a/ryzom/client/src/r2/editor.cpp +++ b/ryzom/client/src/r2/editor.cpp @@ -2988,7 +2988,7 @@ void CEditor::initDecals() // ********************************************************************************************************* -void CEditor::showPrimRender(CPrimRender &dest, const NLMISC::CAABBox &localBox, const NLMISC::CMatrix &worldMat, const CDecalAnim &refDecalAnim) +void CEditor::showPrimRender(CPrimRender &dest, const NLMISC::CAABBox &localBox, const NLMISC::CMatrix &worldMat, const CLegacyDecalAnim &refDecalAnim) { //H_AUTO(R2_CEditor_showPrimRender) CHECK_EDITOR @@ -3055,7 +3055,7 @@ void CEditor::showHighlightDecal(const NLMISC::CVector &pos, float scale) } // ********************************************************************************************************* -void CEditor::showDecal(const NLMISC::CVector2f &pos, float scale, CDecal &decal, const CDecalAnim &decalAnim) +void CEditor::showDecal(const NLMISC::CVector2f &pos, float scale, CLegacyDecal &decal, const CLegacyDecalAnim &decalAnim) { //H_AUTO(R2_CEditor_showDecal) CHECK_EDITOR @@ -4713,7 +4713,7 @@ void CEditor::updateBeforeRender() } // ********************************************************************************************************* -void CEditor::updateDecalBlendRegion(CDecal &decal, const NLMISC::CVector &pos) +void CEditor::updateDecalBlendRegion(CLegacyDecal &decal, const NLMISC::CVector &pos) { //H_AUTO(R2_CEditor_updateDecalBlendRegion) float topBlendDist = CV_DecalTopBlendStartDist.get(); diff --git a/ryzom/client/src/r2/editor.h b/ryzom/client/src/r2/editor.h index 2fc85e1fad..2c138f551d 100644 --- a/ryzom/client/src/r2/editor.h +++ b/ryzom/client/src/r2/editor.h @@ -669,22 +669,22 @@ class CEditor : public NLMISC::CSingleton CTool::TSmartPtr _CurrentTool; static bool _ReloadWanted; // - CDecal _HighlightDecal; - CDecalAnim _HighlightDecalAnim; - CDecal _SelectDecal; - CDecalAnim _SelectDecalAnim; - CDecalAnim _SelectingDecalAnim; - CDecal _PionneerDecal; + CLegacyDecal _HighlightDecal; + CLegacyDecalAnim _HighlightDecalAnim; + CLegacyDecal _SelectDecal; + CLegacyDecalAnim _SelectDecalAnim; + CLegacyDecalAnim _SelectingDecalAnim; + CLegacyDecal _PionneerDecal; // alternative selection for huge element like particle systems, display a box on ground rather than // the selection circle CPrimRender _SelectBox; CPrimRender _HighlightBox; // NLMISC::TTime _LastAutoSaveTime; - CDecalAnim _PionneerDecalAnim; + CLegacyDecalAnim _PionneerDecalAnim; struct CSelectingDecal : public NLMISC::CRefCount { - CDecal Decal; + CLegacyDecal Decal; sint64 EndDate; NLMISC::CVector Pos; float Scale; @@ -924,7 +924,7 @@ class CEditor : public NLMISC::CSingleton void saveCurrentKeySet(); void reloadUI(const char *filename); void initHighlightDecal(); - void updateDecalBlendRegion(CDecal &decal, const NLMISC::CVector &pos); + void updateDecalBlendRegion(CLegacyDecal &decal, const NLMISC::CVector &pos); void initPalette(); void initObjectProjectionMetatable(); void registerDisplayers(); @@ -935,7 +935,7 @@ class CEditor : public NLMISC::CSingleton void registerEnvFunction(const char *name, TLuaWrappedFunction func); // Initialisation of contextual cursor. void initDecals(); - void showDecal(const NLMISC::CVector2f &pos, float scale, CDecal &decal, const CDecalAnim &decalAnim); + void showDecal(const NLMISC::CVector2f &pos, float scale, CLegacyDecal &decal, const CLegacyDecalAnim &decalAnim); void updatePrimitiveContextualVisibility(); void initClassInheritanceTable(); // contextual mouse handling @@ -947,9 +947,9 @@ class CEditor : public NLMISC::CSingleton // update the display of decals created when the player select an instance in the scene void updateSelectingDecals(); // display of highlight or select box (for selection of huge objects) - // the CDecalAnim is used to mimic the color cycle seen when standard selection circle is displayed + // the CLegacyDecalAnim is used to mimic the color cycle seen when standard selection circle is displayed // (no CPrimRenderAnim for now) - void showPrimRender(CPrimRender &dest, const NLMISC::CAABBox &localBox, const NLMISC::CMatrix &worldMat, const CDecalAnim &refDecalAnim); + void showPrimRender(CPrimRender &dest, const NLMISC::CAABBox &localBox, const NLMISC::CMatrix &worldMat, const CLegacyDecalAnim &refDecalAnim); CLuaObject _OldLuaRequestInsertNode; CLuaObject _OldLuaRequestInsertGhostNode; diff --git a/ryzom/client/src/r2/prim_render.cpp b/ryzom/client/src/r2/prim_render.cpp index 93f610377f..048f237048 100644 --- a/ryzom/client/src/r2/prim_render.cpp +++ b/ryzom/client/src/r2/prim_render.cpp @@ -366,7 +366,7 @@ void CPrimRender::update() _VertexDecals.resize(_Vertices.size()); for(uint k = 0; k < _Vertices.size(); ++k) { - _VertexDecals[k] = new CDecal; + _VertexDecals[k] = new CLegacyDecal; _VertexDecals[k]->setClipDownFacing(_Look.ClipDownFacing); if (k == 0 && !_Look.FirstVertexLook.DecalTexture.empty()) { @@ -418,7 +418,7 @@ void CPrimRender::update() _EdgeDecals.resize(_NumEdges); for(sint k = 0; k < _NumEdges; ++k) { - _EdgeDecals[k] = new CDecal; + _EdgeDecals[k] = new CLegacyDecal; _EdgeDecals[k]->setClipDownFacing(_Look.ClipDownFacing); _EdgeDecals[k]->setTexture(_Look.EdgeLook.DecalTexture, _Look.EdgeLook.DecalWrapMode == CEdgeLook::Centered, true, _Look.EdgeLook.DecalFiltered); _EdgeDecals[k]->setDiffuse(_Look.EdgeLook.DecalColor); @@ -541,7 +541,7 @@ void CPrimRender::updateEdge(NL3D::UInstance edge,const NLMISC::CVector &start, } // ********************************************************* -void CPrimRender::updateEdgeDecal(CDecal &edgeDecal, const NLMISC::CVector &start, const NLMISC::CVector &end, float distToStartVertex, float distToEndVertex) +void CPrimRender::updateEdgeDecal(CLegacyDecal &edgeDecal, const NLMISC::CVector &start, const NLMISC::CVector &end, float distToStartVertex, float distToEndVertex) { //H_AUTO(R2_CPrimRender_updateEdgeDecal) CVector2f start2f(start.x, start.y); diff --git a/ryzom/client/src/r2/prim_render.h b/ryzom/client/src/r2/prim_render.h index 7d2f708ed9..3062adfe7c 100644 --- a/ryzom/client/src/r2/prim_render.h +++ b/ryzom/client/src/r2/prim_render.h @@ -225,8 +225,8 @@ class CPrimRender : public CGroupMap::IDeco // CMeshArray _VertexShapeInstances; CMeshArray _EdgeShapeInstances; - std::vector _VertexDecals; - std::vector _EdgeDecals; // Decal of edges on landscape + std::vector _VertexDecals; + std::vector _EdgeDecals; // Decal of edges on landscape NLMISC::CRGBA _Emissive; sint _NumEdges; float _InvWorldTextureWidth; @@ -238,7 +238,7 @@ class CPrimRender : public CGroupMap::IDeco void update(); void updatePos(); void updateEdge(NL3D::UInstance edge, const NLMISC::CVector &start, const NLMISC::CVector &end); - void updateEdgeDecal(CDecal &edgeDecal, const NLMISC::CVector &start, const NLMISC::CVector &end, float distToStartVertex, float distToEndVertex); + void updateEdgeDecal(CLegacyDecal &edgeDecal, const NLMISC::CVector &start, const NLMISC::CVector &end, float distToStartVertex, float distToEndVertex); void forceSetEmissive(NLMISC::CRGBA emissive); // world map display void setWorldMapNumVertices(uint count); diff --git a/ryzom/client/src/r2/tool_choose_pos.h b/ryzom/client/src/r2/tool_choose_pos.h index 913dad3c11..d5032c34bb 100644 --- a/ryzom/client/src/r2/tool_choose_pos.h +++ b/ryzom/client/src/r2/tool_choose_pos.h @@ -92,8 +92,8 @@ class CToolChoosePos : public CTool bool _Valid; NLMISC::CVector _CreatePosition; private: - CDecal _BadPlaceDecal; - CDecal _TestDecal; + CLegacyDecal _BadPlaceDecal; + CLegacyDecal _TestDecal; bool _MultiPos; bool _MultiPosLocked; float _CreateAngle; diff --git a/ryzom/client/src/r2/tool_draw_prim.h b/ryzom/client/src/r2/tool_draw_prim.h index c1b0b43f4f..521a08b194 100644 --- a/ryzom/client/src/r2/tool_draw_prim.h +++ b/ryzom/client/src/r2/tool_draw_prim.h @@ -90,7 +90,7 @@ class CToolDrawPrim : public CTool bool _Extending; bool _Commited; bool _ForceShowPrims; - CDecal _TestDecal; + CLegacyDecal _TestDecal; private: void commit(); void setPrimLook(bool closed, bool lastSegmentValid, bool valid); From 9984e1596c1aedce080d443944de92a7ce0e3437 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 19:05:11 +0000 Subject: [PATCH 04/24] Fix code review issues: bug in addDecal iterator, nlassert misuse, and typos Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/include/nel/3d/decal.h | 4 ++-- nel/src/3d/decal.cpp | 5 ++++- nel/src/3d/decal_manager.cpp | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/nel/include/nel/3d/decal.h b/nel/include/nel/3d/decal.h index 3d5db7215e..73687a294b 100644 --- a/nel/include/nel/3d/decal.h +++ b/nel/include/nel/3d/decal.h @@ -93,7 +93,7 @@ class CDecalContext class CDecal : public CTransform { public: - /** Contructor + /** Constructor */ CDecal(); @@ -101,7 +101,7 @@ class CDecal : public CTransform */ ~CDecal(); - /** Initilization + /** Initialization * Implementation for Ctransform */ void initModel(); diff --git a/nel/src/3d/decal.cpp b/nel/src/3d/decal.cpp index 7312b426ec..0d47b627d5 100644 --- a/nel/src/3d/decal.cpp +++ b/nel/src/3d/decal.cpp @@ -197,7 +197,10 @@ void CDecal::computeDecal(const bool useVertexProgram) /// Verify that coll manager exists CVisualCollisionManager *vcm; if( !(vcm=sc->getVisualCollisionManagerForShadow()) ) - nlassert("VisualCollisionManager does NOT exist, NO decal rendering"); + { + nlerror("VisualCollisionManager does NOT exist, NO decal rendering"); + return; + } ///Preparing shadow map clipping planes diff --git a/nel/src/3d/decal_manager.cpp b/nel/src/3d/decal_manager.cpp index 5a964ab000..00c933d44d 100644 --- a/nel/src/3d/decal_manager.cpp +++ b/nel/src/3d/decal_manager.cpp @@ -61,7 +61,7 @@ void CDecalManager::clearAllDecals() // *************************************************************************** void CDecalManager::addDecal(CDecal *decal, const string &texName) { - TDecalMap::iterator itEnd = _Decals.begin(); + TDecalMap::iterator itEnd = _Decals.end(); TDecalMap::iterator it = _Decals.find(texName); if (it!=itEnd)///this key exist, add decal to vector { From 8ee3ca1bfe15d67e3d821ea234557ecbaf5f0ed1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 19:18:56 +0000 Subject: [PATCH 05/24] =?UTF-8?q?Rename=20ryzom=20client=20decal=20files?= =?UTF-8?q?=20to=20match=20renamed=20classes=20(decal=20=E2=86=92=20legacy?= =?UTF-8?q?=5Fdecal)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- ryzom/client/src/CMakeLists.txt | 4 ++-- ryzom/client/src/landscape_poly_drawer.cpp | 2 +- ryzom/client/src/{decal.cpp => legacy_decal.cpp} | 2 +- ryzom/client/src/{decal.h => legacy_decal.h} | 0 ryzom/client/src/{decal_anim.cpp => legacy_decal_anim.cpp} | 4 ++-- ryzom/client/src/{decal_anim.h => legacy_decal_anim.h} | 0 ryzom/client/src/r2/displayer_visual.h | 2 +- ryzom/client/src/r2/displayer_visual_activity_sequence.h | 2 +- ryzom/client/src/r2/displayer_visual_entity.h | 2 +- ryzom/client/src/r2/editor.h | 4 ++-- ryzom/client/src/r2/tool_choose_pos.h | 2 +- ryzom/client/src/r2/tool_create_entity.h | 2 +- ryzom/client/src/r2/tool_draw_prim.h | 2 +- 13 files changed, 14 insertions(+), 14 deletions(-) rename ryzom/client/src/{decal.cpp => legacy_decal.cpp} (99%) rename ryzom/client/src/{decal.h => legacy_decal.h} (100%) rename ryzom/client/src/{decal_anim.cpp => legacy_decal_anim.cpp} (97%) rename ryzom/client/src/{decal_anim.h => legacy_decal_anim.h} (100%) diff --git a/ryzom/client/src/CMakeLists.txt b/ryzom/client/src/CMakeLists.txt index 1ac4d3594a..607c4118f0 100644 --- a/ryzom/client/src/CMakeLists.txt +++ b/ryzom/client/src/CMakeLists.txt @@ -90,8 +90,8 @@ IF(WITH_RYZOM_CLIENT) ) SOURCE_GROUP("outposts" FILES ${RZCLIENT_OUTPOSTS}) FILE(GLOB RZCLIENT_DECALS - decal.cpp decal.h - decal*.cpp decal_*.h + legacy_decal.cpp legacy_decal.h + legacy_decal*.cpp legacy_decal_*.h ) SOURCE_GROUP("decals" FILES ${RZCLIENT_DECALS}) diff --git a/ryzom/client/src/landscape_poly_drawer.cpp b/ryzom/client/src/landscape_poly_drawer.cpp index 67ca596795..1b33a55e60 100644 --- a/ryzom/client/src/landscape_poly_drawer.cpp +++ b/ryzom/client/src/landscape_poly_drawer.cpp @@ -27,7 +27,7 @@ #include "nel/3d/driver_user.h" // client -#include "decal.h" +#include "legacy_decal.h" using namespace NLMISC; using namespace NL3D; diff --git a/ryzom/client/src/decal.cpp b/ryzom/client/src/legacy_decal.cpp similarity index 99% rename from ryzom/client/src/decal.cpp rename to ryzom/client/src/legacy_decal.cpp index 6044f8fd43..0a13d88dfb 100644 --- a/ryzom/client/src/decal.cpp +++ b/ryzom/client/src/legacy_decal.cpp @@ -19,7 +19,7 @@ // along with this program. If not, see . #include "stdpch.h" -#include "decal.h" +#include "legacy_decal.h" // #include "nel/3d/shadow_map.h" #include "nel/3d/texture_file.h" diff --git a/ryzom/client/src/decal.h b/ryzom/client/src/legacy_decal.h similarity index 100% rename from ryzom/client/src/decal.h rename to ryzom/client/src/legacy_decal.h diff --git a/ryzom/client/src/decal_anim.cpp b/ryzom/client/src/legacy_decal_anim.cpp similarity index 97% rename from ryzom/client/src/decal_anim.cpp rename to ryzom/client/src/legacy_decal_anim.cpp index 21c8a46b69..9773cddeb4 100644 --- a/ryzom/client/src/decal_anim.cpp +++ b/ryzom/client/src/legacy_decal_anim.cpp @@ -19,8 +19,8 @@ #include "stdpch.h" // -#include "decal_anim.h" -#include "decal.h" +#include "legacy_decal_anim.h" +#include "legacy_decal.h" #include "nel/gui/lua_ihm.h" #include "nel/gui/lua_object.h" // diff --git a/ryzom/client/src/decal_anim.h b/ryzom/client/src/legacy_decal_anim.h similarity index 100% rename from ryzom/client/src/decal_anim.h rename to ryzom/client/src/legacy_decal_anim.h diff --git a/ryzom/client/src/r2/displayer_visual.h b/ryzom/client/src/r2/displayer_visual.h index 844f2a2ff3..90aeb1a7e9 100644 --- a/ryzom/client/src/r2/displayer_visual.h +++ b/ryzom/client/src/r2/displayer_visual.h @@ -25,7 +25,7 @@ #include "nel/misc/vectord.h" #include "nel/misc/rgba.h" // -#include "../decal.h" +#include "../legacy_decal.h" class CGroupInScene; diff --git a/ryzom/client/src/r2/displayer_visual_activity_sequence.h b/ryzom/client/src/r2/displayer_visual_activity_sequence.h index fe7d490159..45c63e8502 100644 --- a/ryzom/client/src/r2/displayer_visual_activity_sequence.h +++ b/ryzom/client/src/r2/displayer_visual_activity_sequence.h @@ -18,7 +18,7 @@ #define R2_DISPLAYER_VISUAL_ACTIVITY_SEQUENCE_H #include "displayer_visual.h" -#include "../decal.h" +#include "../legacy_decal.h" #include "editor.h" #include "nel/misc/line.h" // diff --git a/ryzom/client/src/r2/displayer_visual_entity.h b/ryzom/client/src/r2/displayer_visual_entity.h index f5bf4ea94c..6ca49b7e7b 100644 --- a/ryzom/client/src/r2/displayer_visual_entity.h +++ b/ryzom/client/src/r2/displayer_visual_entity.h @@ -22,7 +22,7 @@ #include "displayer_visual.h" #include "instance.h" -#include "../decal.h" +#include "../legacy_decal.h" #include "nel/gui/lua_object.h" #include "instance_map_deco.h" diff --git a/ryzom/client/src/r2/editor.h b/ryzom/client/src/r2/editor.h index 2c138f551d..5140ec4260 100644 --- a/ryzom/client/src/r2/editor.h +++ b/ryzom/client/src/r2/editor.h @@ -23,8 +23,8 @@ #include "nel/gui/lua_object.h" #include "instance.h" #include "tool.h" -#include "../decal.h" -#include "../decal_anim.h" +#include "../legacy_decal.h" +#include "../legacy_decal_anim.h" #include "entity_custom_select_box.h" #include "island_collision.h" #include "prim_render.h" diff --git a/ryzom/client/src/r2/tool_choose_pos.h b/ryzom/client/src/r2/tool_choose_pos.h index d5032c34bb..0ae2a8a677 100644 --- a/ryzom/client/src/r2/tool_choose_pos.h +++ b/ryzom/client/src/r2/tool_choose_pos.h @@ -21,7 +21,7 @@ #define R2_TOOL_CHOOSE_POS_H #include "tool.h" -#include "../decal.h" +#include "../legacy_decal.h" #include "prim_render.h" #include "nel/misc/vector.h" #include "nel/misc/polygon.h" diff --git a/ryzom/client/src/r2/tool_create_entity.h b/ryzom/client/src/r2/tool_create_entity.h index a6c174b223..eca2e2f56e 100644 --- a/ryzom/client/src/r2/tool_create_entity.h +++ b/ryzom/client/src/r2/tool_create_entity.h @@ -23,7 +23,7 @@ #include "tool_choose_pos.h" #include "nel/misc/vector.h" #include "prim_render.h" -#include "../decal.h" +#include "../legacy_decal.h" #include "auto_group.h" #include "displayer_visual_entity.h" diff --git a/ryzom/client/src/r2/tool_draw_prim.h b/ryzom/client/src/r2/tool_draw_prim.h index 521a08b194..9d14e471f3 100644 --- a/ryzom/client/src/r2/tool_draw_prim.h +++ b/ryzom/client/src/r2/tool_draw_prim.h @@ -22,7 +22,7 @@ #include "tool.h" #include "prim_render.h" -#include "../decal.h" +#include "../legacy_decal.h" // #include "nel/misc/vector.h" From 912f22f84a1e1b11b2c653298bbe46ae3c6dd743 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 19:58:19 +0000 Subject: [PATCH 06/24] Plan: Implement final decal design from PDF pseudocode Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- .../lua_ide_dll_nevrax/source/libapi/libapi.c | 294 ++-- .../source/standard/lauxlib.c | 1202 ++++++------- .../source/standard/lbaselib.c | 1368 +++++++-------- .../source/standard/ldblib.c | 618 +++---- .../source/standard/liolib.c | 1520 ++++++++-------- .../source/standard/lmathlib.c | 512 +++--- .../source/standard/loadlib.c | 410 ++--- .../source/standard/lstrlib.c | 1560 ++++++++--------- .../source/standard/ltablib.c | 520 +++--- .../src/ai_service/doc/doxycfg/doxygen.css | 124 +- 10 files changed, 4064 insertions(+), 4064 deletions(-) diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/libapi/libapi.c b/ryzom/client/src/lua_ide_dll_nevrax/source/libapi/libapi.c index ddc5c620e3..5f2a007d46 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/libapi/libapi.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/libapi/libapi.c @@ -1,147 +1,147 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#include "stdafx.h" - -#include "../ide2/DebuggerMessages.h" - -static HWND g_hWnd; - -using namespace std; - -// maximum mumber of lines the output console should have -static const WORD MAX_CONSOLE_LINES = 500; - -BOOL isWindowsNT(void) -{ - static BOOL once = FALSE; - static BOOL isNT = FALSE; - - if (!once) - { - OSVERSIONINFO osver; - osver.dwOSVersionInfoSize = sizeof(osver); - if (GetVersionEx(&osver)) - if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) - isNT = TRUE; - once = TRUE; - } - return isNT; -} - -static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd) -{ - char tmp[20], *tty; - if (isWindowsNT()) - tty = "ConsoleWindowClass"; - else - tty = "tty"; - if (GetClassName(wnd, tmp, sizeof(tmp)) && !strcmp(tmp, tty)) - { - DWORD wndproc, thisproc = GetCurrentProcessId(); - GetWindowThreadProcessId(wnd, &wndproc); - if (wndproc == thisproc) { - *((HWND*)retwnd) = wnd; - return FALSE; - } - } - return TRUE; -} - - -static BOOL CtrlHandler(DWORD fdwCtrlType) -{ - switch (fdwCtrlType) - { - // Handle the CTRL+C signal. - - case CTRL_C_EVENT: - case CTRL_BREAK_EVENT: - return TRUE; - - // CTRL+CLOSE: confirm that the user wants to exit. - - case CTRL_CLOSE_EVENT: - - case CTRL_LOGOFF_EVENT: - - case CTRL_SHUTDOWN_EVENT: - - default: - - return FALSE; - } -} - -void AttachConsole() -{ - int hConHandle; - long lStdHandle; - CONSOLE_SCREEN_BUFFER_INFO coninfo; - FILE *fp; - HWND hWndConsole; - HMENU hSysMenu; - -// allocate a console for this app - FreeConsole(); - AllocConsole(); - -//disable system menu -> close box - EnumWindows(EnumttyWindow, (long)(&hWndConsole)); - hSysMenu = GetSystemMenu(hWndConsole, FALSE); - ModifyMenu(hSysMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED, 0, 0); - ShowWindow(hWndConsole, SW_SHOW); - -// set the screen buffer to be big enough to let us scroll text - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); - coninfo.dwSize.Y = MAX_CONSOLE_LINES; - SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); - -// redirect unbuffered STDOUT to the console - lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE); - hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); - fp = _fdopen( hConHandle, "w" ); - *stdout = *fp; - setvbuf( stdout, NULL, _IONBF, 0 ); - -// redirect unbuffered STDIN to the console - lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE); - hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); - ::SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_MOUSE_INPUT); - fp = _fdopen( hConHandle, "r" ); - *stdin = *fp; - setvbuf( stdin, NULL, _IONBF, 0 ); - -// redirect unbuffered STDERR to the console - lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE); - hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); - fp = _fdopen( hConHandle, "w" ); - *stderr = *fp; - setvbuf( stderr, NULL, _IONBF, 0 ); - -// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog -// point to console as well - ios::sync_with_stdio(); - - SetConsoleCtrlHandler( - (PHANDLER_ROUTINE) CtrlHandler, // handler function - TRUE); // add to list -} - -BOOL InitLibAPI(HWND hWnd) -{ - g_hWnd = hWnd; -} +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdafx.h" + +#include "../ide2/DebuggerMessages.h" + +static HWND g_hWnd; + +using namespace std; + +// maximum mumber of lines the output console should have +static const WORD MAX_CONSOLE_LINES = 500; + +BOOL isWindowsNT(void) +{ + static BOOL once = FALSE; + static BOOL isNT = FALSE; + + if (!once) + { + OSVERSIONINFO osver; + osver.dwOSVersionInfoSize = sizeof(osver); + if (GetVersionEx(&osver)) + if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) + isNT = TRUE; + once = TRUE; + } + return isNT; +} + +static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd) +{ + char tmp[20], *tty; + if (isWindowsNT()) + tty = "ConsoleWindowClass"; + else + tty = "tty"; + if (GetClassName(wnd, tmp, sizeof(tmp)) && !strcmp(tmp, tty)) + { + DWORD wndproc, thisproc = GetCurrentProcessId(); + GetWindowThreadProcessId(wnd, &wndproc); + if (wndproc == thisproc) { + *((HWND*)retwnd) = wnd; + return FALSE; + } + } + return TRUE; +} + + +static BOOL CtrlHandler(DWORD fdwCtrlType) +{ + switch (fdwCtrlType) + { + // Handle the CTRL+C signal. + + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + return TRUE; + + // CTRL+CLOSE: confirm that the user wants to exit. + + case CTRL_CLOSE_EVENT: + + case CTRL_LOGOFF_EVENT: + + case CTRL_SHUTDOWN_EVENT: + + default: + + return FALSE; + } +} + +void AttachConsole() +{ + int hConHandle; + long lStdHandle; + CONSOLE_SCREEN_BUFFER_INFO coninfo; + FILE *fp; + HWND hWndConsole; + HMENU hSysMenu; + +// allocate a console for this app + FreeConsole(); + AllocConsole(); + +//disable system menu -> close box + EnumWindows(EnumttyWindow, (long)(&hWndConsole)); + hSysMenu = GetSystemMenu(hWndConsole, FALSE); + ModifyMenu(hSysMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED, 0, 0); + ShowWindow(hWndConsole, SW_SHOW); + +// set the screen buffer to be big enough to let us scroll text + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); + coninfo.dwSize.Y = MAX_CONSOLE_LINES; + SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); + +// redirect unbuffered STDOUT to the console + lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE); + hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + fp = _fdopen( hConHandle, "w" ); + *stdout = *fp; + setvbuf( stdout, NULL, _IONBF, 0 ); + +// redirect unbuffered STDIN to the console + lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE); + hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + ::SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_MOUSE_INPUT); + fp = _fdopen( hConHandle, "r" ); + *stdin = *fp; + setvbuf( stdin, NULL, _IONBF, 0 ); + +// redirect unbuffered STDERR to the console + lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE); + hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + fp = _fdopen( hConHandle, "w" ); + *stderr = *fp; + setvbuf( stderr, NULL, _IONBF, 0 ); + +// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog +// point to console as well + ios::sync_with_stdio(); + + SetConsoleCtrlHandler( + (PHANDLER_ROUTINE) CtrlHandler, // handler function + TRUE); // add to list +} + +BOOL InitLibAPI(HWND hWnd) +{ + g_hWnd = hWnd; +} diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lauxlib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lauxlib.c index adc01cb898..1ef9877354 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lauxlib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lauxlib.c @@ -1,601 +1,601 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include -#include -#include -#include -#include - - -/* This file uses only the official API of Lua. -** Any function declared here could be written as an application function. -*/ - -#define lauxlib_c - -#include "lua.h" - -#include "lauxlib.h" - - -/* number of prereserved references (for internal use) */ -#define RESERVED_REFS 2 - -/* reserved references */ -#define FREELIST_REF 1 /* free list of references */ -#define ARRAYSIZE_REF 2 /* array sizes */ - - -/* convert a stack index to positive */ -#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \ - lua_gettop(L) + (i) + 1) - - -/* -** {====================================================== -** Error-report functions -** ======================================================= -*/ - - -LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) { - lua_Debug ar; - lua_getstack(L, 0, &ar); - lua_getinfo(L, "n", &ar); - if (strcmp(ar.namewhat, "method") == 0) { - narg--; /* do not count `self' */ - if (narg == 0) /* error is in the self argument itself? */ - return luaL_error(L, "calling `%s' on bad self (%s)", ar.name, extramsg); - } - if (ar.name == NULL) - ar.name = "?"; - return luaL_error(L, "bad argument #%d to `%s' (%s)", - narg, ar.name, extramsg); -} - - -LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) { - const char *msg = lua_pushfstring(L, "%s expected, got %s", - tname, lua_typename(L, lua_type(L,narg))); - return luaL_argerror(L, narg, msg); -} - - -static void tag_error (lua_State *L, int narg, int tag) { - luaL_typerror(L, narg, lua_typename(L, tag)); -} - - -LUALIB_API void luaL_where (lua_State *L, int level) { - lua_Debug ar; - if (lua_getstack(L, level, &ar)) { /* check function at level */ - lua_getinfo(L, "Snl", &ar); /* get info about it */ - if (ar.currentline > 0) { /* is there info? */ - lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); - return; - } - } - lua_pushliteral(L, ""); /* else, no information available... */ -} - - -LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { - va_list argp; - va_start(argp, fmt); - luaL_where(L, 1); - lua_pushvfstring(L, fmt, argp); - va_end(argp); - lua_concat(L, 2); - return lua_error(L); -} - -/* }====================================================== */ - - -LUALIB_API int luaL_findstring (const char *name, const char *const list[]) { - int i; - for (i=0; list[i]; i++) - if (strcmp(list[i], name) == 0) - return i; - return -1; /* name not found */ -} - - -LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { - lua_pushstring(L, tname); - lua_rawget(L, LUA_REGISTRYINDEX); /* get registry.name */ - if (!lua_isnil(L, -1)) /* name already in use? */ - return 0; /* leave previous value on top, but return 0 */ - lua_pop(L, 1); - lua_newtable(L); /* create metatable */ - lua_pushstring(L, tname); - lua_pushvalue(L, -2); - lua_rawset(L, LUA_REGISTRYINDEX); /* registry.name = metatable */ - lua_pushvalue(L, -1); - lua_pushstring(L, tname); - lua_rawset(L, LUA_REGISTRYINDEX); /* registry[metatable] = name */ - return 1; -} - - -LUALIB_API void luaL_getmetatable (lua_State *L, const char *tname) { - lua_pushstring(L, tname); - lua_rawget(L, LUA_REGISTRYINDEX); -} - - -LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { - const char *tn; - if (!lua_getmetatable(L, ud)) return NULL; /* no metatable? */ - lua_rawget(L, LUA_REGISTRYINDEX); /* get registry[metatable] */ - tn = lua_tostring(L, -1); - if (tn && (strcmp(tn, tname) == 0)) { - lua_pop(L, 1); - return lua_touserdata(L, ud); - } - else { - lua_pop(L, 1); - return NULL; - } -} - - -LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) { - if (!lua_checkstack(L, space)) - luaL_error(L, "stack overflow (%s)", mes); -} - - -LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) { - if (lua_type(L, narg) != t) - tag_error(L, narg, t); -} - - -LUALIB_API void luaL_checkany (lua_State *L, int narg) { - if (lua_type(L, narg) == LUA_TNONE) - luaL_argerror(L, narg, "value expected"); -} - - -LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) { - const char *s = lua_tostring(L, narg); - if (!s) tag_error(L, narg, LUA_TSTRING); - if (len) *len = lua_strlen(L, narg); - return s; -} - - -LUALIB_API const char *luaL_optlstring (lua_State *L, int narg, - const char *def, size_t *len) { - if (lua_isnoneornil(L, narg)) { - if (len) - *len = (def ? strlen(def) : 0); - return def; - } - else return luaL_checklstring(L, narg, len); -} - - -LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) { - lua_Number d = lua_tonumber(L, narg); - if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ - tag_error(L, narg, LUA_TNUMBER); - return d; -} - - -LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) { - if (lua_isnoneornil(L, narg)) return def; - else return luaL_checknumber(L, narg); -} - - -LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { - if (!lua_getmetatable(L, obj)) /* no metatable? */ - return 0; - lua_pushstring(L, event); - lua_rawget(L, -2); - if (lua_isnil(L, -1)) { - lua_pop(L, 2); /* remove metatable and metafield */ - return 0; - } - else { - lua_remove(L, -2); /* remove only metatable */ - return 1; - } -} - - -LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { - obj = abs_index(L, obj); - if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ - return 0; - lua_pushvalue(L, obj); - lua_call(L, 1, 1); - return 1; -} - - -LUALIB_API void luaL_openlib (lua_State *L, const char *libname, - const luaL_reg *l, int nup) { - if (libname) { - lua_pushstring(L, libname); - lua_gettable(L, LUA_GLOBALSINDEX); /* check whether lib already exists */ - if (lua_isnil(L, -1)) { /* no? */ - lua_pop(L, 1); - lua_newtable(L); /* create it */ - lua_pushstring(L, libname); - lua_pushvalue(L, -2); - lua_settable(L, LUA_GLOBALSINDEX); /* register it with given name */ - } - lua_insert(L, -(nup+1)); /* move library table to below upvalues */ - } - for (; l->name; l++) { - int i; - lua_pushstring(L, l->name); - for (i=0; ifunc, nup); - lua_settable(L, -(nup+3)); - } - lua_pop(L, nup); /* remove upvalues */ -} - - - -/* -** {====================================================== -** getn-setn: size for arrays -** ======================================================= -*/ - -static int checkint (lua_State *L, int topop) { - int n = (int)lua_tonumber(L, -1); - if (n == 0 && !lua_isnumber(L, -1)) n = -1; - lua_pop(L, topop); - return n; -} - - -static void getsizes (lua_State *L) { - lua_rawgeti(L, LUA_REGISTRYINDEX, ARRAYSIZE_REF); - if (lua_isnil(L, -1)) { /* no `size' table? */ - lua_pop(L, 1); /* remove nil */ - lua_newtable(L); /* create it */ - lua_pushvalue(L, -1); /* `size' will be its own metatable */ - lua_setmetatable(L, -2); - lua_pushliteral(L, "__mode"); - lua_pushliteral(L, "k"); - lua_rawset(L, -3); /* metatable(N).__mode = "k" */ - lua_pushvalue(L, -1); - lua_rawseti(L, LUA_REGISTRYINDEX, ARRAYSIZE_REF); /* store in register */ - } -} - - -void luaL_setn (lua_State *L, int t, int n) { - t = abs_index(L, t); - lua_pushliteral(L, "n"); - lua_rawget(L, t); - if (checkint(L, 1) >= 0) { /* is there a numeric field `n'? */ - lua_pushliteral(L, "n"); /* use it */ - lua_pushnumber(L, (lua_Number)n); - lua_rawset(L, t); - } - else { /* use `sizes' */ - getsizes(L); - lua_pushvalue(L, t); - lua_pushnumber(L, (lua_Number)n); - lua_rawset(L, -3); /* sizes[t] = n */ - lua_pop(L, 1); /* remove `sizes' */ - } -} - - -int luaL_getn (lua_State *L, int t) { - int n; - t = abs_index(L, t); - lua_pushliteral(L, "n"); /* try t.n */ - lua_rawget(L, t); - if ((n = checkint(L, 1)) >= 0) return n; - getsizes(L); /* else try sizes[t] */ - lua_pushvalue(L, t); - lua_rawget(L, -2); - if ((n = checkint(L, 2)) >= 0) return n; - for (n = 1; ; n++) { /* else must count elements */ - lua_rawgeti(L, t, n); - if (lua_isnil(L, -1)) break; - lua_pop(L, 1); - } - lua_pop(L, 1); - return n - 1; -} - -/* }====================================================== */ - - - -/* -** {====================================================== -** Generic Buffer manipulation -** ======================================================= -*/ - - -#define bufflen(B) ((B)->p - (B)->buffer) -#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B))) - -#define LIMIT (LUA_MINSTACK/2) - - -static int emptybuffer (luaL_Buffer *B) { - size_t l = bufflen(B); - if (l == 0) return 0; /* put nothing on stack */ - else { - lua_pushlstring(B->L, B->buffer, l); - B->p = B->buffer; - B->lvl++; - return 1; - } -} - - -static void adjuststack (luaL_Buffer *B) { - if (B->lvl > 1) { - lua_State *L = B->L; - int toget = 1; /* number of levels to concat */ - size_t toplen = lua_strlen(L, -1); - do { - size_t l = lua_strlen(L, -(toget+1)); - if (B->lvl - toget + 1 >= LIMIT || toplen > l) { - toplen += l; - toget++; - } - else break; - } while (toget < B->lvl); - lua_concat(L, toget); - B->lvl = B->lvl - toget + 1; - } -} - - -LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) { - if (emptybuffer(B)) - adjuststack(B); - return B->buffer; -} - - -LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { - while (l--) - luaL_putchar(B, *s++); -} - - -LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { - luaL_addlstring(B, s, strlen(s)); -} - - -LUALIB_API void luaL_pushresult (luaL_Buffer *B) { - emptybuffer(B); - lua_concat(B->L, B->lvl); - B->lvl = 1; -} - - -LUALIB_API void luaL_addvalue (luaL_Buffer *B) { - lua_State *L = B->L; - size_t vl = lua_strlen(L, -1); - if (vl <= bufffree(B)) { /* fit into buffer? */ - memcpy(B->p, lua_tostring(L, -1), vl); /* put it there */ - B->p += vl; - lua_pop(L, 1); /* remove from stack */ - } - else { - if (emptybuffer(B)) - lua_insert(L, -2); /* put buffer before new value */ - B->lvl++; /* add new value into B stack */ - adjuststack(B); - } -} - - -LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { - B->L = L; - B->p = B->buffer; - B->lvl = 0; -} - -/* }====================================================== */ - - -LUALIB_API int luaL_ref (lua_State *L, int t) { - int ref; - t = abs_index(L, t); - if (lua_isnil(L, -1)) { - lua_pop(L, 1); /* remove from stack */ - return LUA_REFNIL; /* `nil' has a unique fixed reference */ - } - lua_rawgeti(L, t, FREELIST_REF); /* get first free element */ - ref = (int)lua_tonumber(L, -1); /* ref = t[FREELIST_REF] */ - lua_pop(L, 1); /* remove it from stack */ - if (ref != 0) { /* any free element? */ - lua_rawgeti(L, t, ref); /* remove it from list */ - lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */ - } - else { /* no free elements */ - ref = luaL_getn(L, t); - if (ref < RESERVED_REFS) - ref = RESERVED_REFS; /* skip reserved references */ - ref++; /* create new reference */ - luaL_setn(L, t, ref); - } - lua_rawseti(L, t, ref); - return ref; -} - - -LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { - if (ref >= 0) { - t = abs_index(L, t); - lua_rawgeti(L, t, FREELIST_REF); - lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */ - lua_pushnumber(L, (lua_Number)ref); - lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */ - } -} - - - -/* -** {====================================================== -** Load functions -** ======================================================= -*/ - -typedef struct LoadF { - FILE *f; - char buff[LUAL_BUFFERSIZE]; -} LoadF; - - -static const char *getF (lua_State *L, void *ud, size_t *size) { - LoadF *lf = (LoadF *)ud; - (void)L; - if (feof(lf->f)) return NULL; - *size = fread(lf->buff, 1, LUAL_BUFFERSIZE, lf->f); - return (*size > 0) ? lf->buff : NULL; -} - - -static int errfile (lua_State *L, int fnameindex) { - const char *filename = lua_tostring(L, fnameindex) + 1; - lua_pushfstring(L, "cannot read %s: %s", filename, strerror(errno)); - lua_remove(L, fnameindex); - return LUA_ERRFILE; -} - - -LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) { - LoadF lf; - int status, readstatus; - int c; - int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ - if (filename == NULL) { - lua_pushliteral(L, "=stdin"); - lf.f = stdin; - } - else { - lua_pushfstring(L, "@%s", filename); - lf.f = fopen(filename, "r"); - } - if (lf.f == NULL) return errfile(L, fnameindex); /* unable to open file */ - c = ungetc(getc(lf.f), lf.f); - if (!(isspace(c) || isprint(c)) && lf.f != stdin) { /* binary file? */ - fclose(lf.f); - lf.f = fopen(filename, "rb"); /* reopen in binary mode */ - if (lf.f == NULL) return errfile(L, fnameindex); /* unable to reopen file */ - } - status = lua_load(L, getF, &lf, lua_tostring(L, -1)); - readstatus = ferror(lf.f); - if (lf.f != stdin) fclose(lf.f); /* close file (even in case of errors) */ - if (readstatus) { - lua_settop(L, fnameindex); /* ignore results from `lua_load' */ - return errfile(L, fnameindex); - } - lua_remove(L, fnameindex); - return status; -} - - -typedef struct LoadS { - const char *s; - size_t size; -} LoadS; - - -static const char *getS (lua_State *L, void *ud, size_t *size) { - LoadS *ls = (LoadS *)ud; - (void)L; - if (ls->size == 0) return NULL; - *size = ls->size; - ls->size = 0; - return ls->s; -} - - -LUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size, - const char *name) { - LoadS ls; - ls.s = buff; - ls.size = size; - return lua_load(L, getS, &ls, name); -} - -/* }====================================================== */ - - -/* -** {====================================================== -** compatibility code -** ======================================================= -*/ - - -static void callalert (lua_State *L, int status) { - if (status != 0) { - lua_getglobal(L, "_ALERT"); - if (lua_isfunction(L, -1)) { - lua_insert(L, -2); - lua_call(L, 1, 0); - } - else { /* no _ALERT function; print it on stderr */ - fprintf(stderr, "%s\n", lua_tostring(L, -2)); - lua_pop(L, 2); /* remove error message and _ALERT */ - } - } -} - - -static int aux_do (lua_State *L, int status) { - if (status == 0) { /* parse OK? */ - status = lua_pcall(L, 0, LUA_MULTRET, 0); /* call main */ - } - callalert(L, status); - return status; -} - - -LUALIB_API int lua_dofile (lua_State *L, const char *filename) { - return aux_do(L, luaL_loadfile(L, filename)); -} - - -LUALIB_API int lua_dobuffer (lua_State *L, const char *buff, size_t size, - const char *name) { - return aux_do(L, luaL_loadbuffer(L, buff, size, name)); -} - - -LUALIB_API int lua_dostring (lua_State *L, const char *str) { - return lua_dobuffer(L, str, strlen(str), str); -} - -/* }====================================================== */ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include +#include +#include +#include +#include + + +/* This file uses only the official API of Lua. +** Any function declared here could be written as an application function. +*/ + +#define lauxlib_c + +#include "lua.h" + +#include "lauxlib.h" + + +/* number of prereserved references (for internal use) */ +#define RESERVED_REFS 2 + +/* reserved references */ +#define FREELIST_REF 1 /* free list of references */ +#define ARRAYSIZE_REF 2 /* array sizes */ + + +/* convert a stack index to positive */ +#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \ + lua_gettop(L) + (i) + 1) + + +/* +** {====================================================== +** Error-report functions +** ======================================================= +*/ + + +LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) { + lua_Debug ar; + lua_getstack(L, 0, &ar); + lua_getinfo(L, "n", &ar); + if (strcmp(ar.namewhat, "method") == 0) { + narg--; /* do not count `self' */ + if (narg == 0) /* error is in the self argument itself? */ + return luaL_error(L, "calling `%s' on bad self (%s)", ar.name, extramsg); + } + if (ar.name == NULL) + ar.name = "?"; + return luaL_error(L, "bad argument #%d to `%s' (%s)", + narg, ar.name, extramsg); +} + + +LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) { + const char *msg = lua_pushfstring(L, "%s expected, got %s", + tname, lua_typename(L, lua_type(L,narg))); + return luaL_argerror(L, narg, msg); +} + + +static void tag_error (lua_State *L, int narg, int tag) { + luaL_typerror(L, narg, lua_typename(L, tag)); +} + + +LUALIB_API void luaL_where (lua_State *L, int level) { + lua_Debug ar; + if (lua_getstack(L, level, &ar)) { /* check function at level */ + lua_getinfo(L, "Snl", &ar); /* get info about it */ + if (ar.currentline > 0) { /* is there info? */ + lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); + return; + } + } + lua_pushliteral(L, ""); /* else, no information available... */ +} + + +LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + luaL_where(L, 1); + lua_pushvfstring(L, fmt, argp); + va_end(argp); + lua_concat(L, 2); + return lua_error(L); +} + +/* }====================================================== */ + + +LUALIB_API int luaL_findstring (const char *name, const char *const list[]) { + int i; + for (i=0; list[i]; i++) + if (strcmp(list[i], name) == 0) + return i; + return -1; /* name not found */ +} + + +LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { + lua_pushstring(L, tname); + lua_rawget(L, LUA_REGISTRYINDEX); /* get registry.name */ + if (!lua_isnil(L, -1)) /* name already in use? */ + return 0; /* leave previous value on top, but return 0 */ + lua_pop(L, 1); + lua_newtable(L); /* create metatable */ + lua_pushstring(L, tname); + lua_pushvalue(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); /* registry.name = metatable */ + lua_pushvalue(L, -1); + lua_pushstring(L, tname); + lua_rawset(L, LUA_REGISTRYINDEX); /* registry[metatable] = name */ + return 1; +} + + +LUALIB_API void luaL_getmetatable (lua_State *L, const char *tname) { + lua_pushstring(L, tname); + lua_rawget(L, LUA_REGISTRYINDEX); +} + + +LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { + const char *tn; + if (!lua_getmetatable(L, ud)) return NULL; /* no metatable? */ + lua_rawget(L, LUA_REGISTRYINDEX); /* get registry[metatable] */ + tn = lua_tostring(L, -1); + if (tn && (strcmp(tn, tname) == 0)) { + lua_pop(L, 1); + return lua_touserdata(L, ud); + } + else { + lua_pop(L, 1); + return NULL; + } +} + + +LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) { + if (!lua_checkstack(L, space)) + luaL_error(L, "stack overflow (%s)", mes); +} + + +LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) { + if (lua_type(L, narg) != t) + tag_error(L, narg, t); +} + + +LUALIB_API void luaL_checkany (lua_State *L, int narg) { + if (lua_type(L, narg) == LUA_TNONE) + luaL_argerror(L, narg, "value expected"); +} + + +LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) { + const char *s = lua_tostring(L, narg); + if (!s) tag_error(L, narg, LUA_TSTRING); + if (len) *len = lua_strlen(L, narg); + return s; +} + + +LUALIB_API const char *luaL_optlstring (lua_State *L, int narg, + const char *def, size_t *len) { + if (lua_isnoneornil(L, narg)) { + if (len) + *len = (def ? strlen(def) : 0); + return def; + } + else return luaL_checklstring(L, narg, len); +} + + +LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) { + lua_Number d = lua_tonumber(L, narg); + if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ + tag_error(L, narg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) { + if (lua_isnoneornil(L, narg)) return def; + else return luaL_checknumber(L, narg); +} + + +LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { + if (!lua_getmetatable(L, obj)) /* no metatable? */ + return 0; + lua_pushstring(L, event); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); /* remove metatable and metafield */ + return 0; + } + else { + lua_remove(L, -2); /* remove only metatable */ + return 1; + } +} + + +LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { + obj = abs_index(L, obj); + if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ + return 0; + lua_pushvalue(L, obj); + lua_call(L, 1, 1); + return 1; +} + + +LUALIB_API void luaL_openlib (lua_State *L, const char *libname, + const luaL_reg *l, int nup) { + if (libname) { + lua_pushstring(L, libname); + lua_gettable(L, LUA_GLOBALSINDEX); /* check whether lib already exists */ + if (lua_isnil(L, -1)) { /* no? */ + lua_pop(L, 1); + lua_newtable(L); /* create it */ + lua_pushstring(L, libname); + lua_pushvalue(L, -2); + lua_settable(L, LUA_GLOBALSINDEX); /* register it with given name */ + } + lua_insert(L, -(nup+1)); /* move library table to below upvalues */ + } + for (; l->name; l++) { + int i; + lua_pushstring(L, l->name); + for (i=0; ifunc, nup); + lua_settable(L, -(nup+3)); + } + lua_pop(L, nup); /* remove upvalues */ +} + + + +/* +** {====================================================== +** getn-setn: size for arrays +** ======================================================= +*/ + +static int checkint (lua_State *L, int topop) { + int n = (int)lua_tonumber(L, -1); + if (n == 0 && !lua_isnumber(L, -1)) n = -1; + lua_pop(L, topop); + return n; +} + + +static void getsizes (lua_State *L) { + lua_rawgeti(L, LUA_REGISTRYINDEX, ARRAYSIZE_REF); + if (lua_isnil(L, -1)) { /* no `size' table? */ + lua_pop(L, 1); /* remove nil */ + lua_newtable(L); /* create it */ + lua_pushvalue(L, -1); /* `size' will be its own metatable */ + lua_setmetatable(L, -2); + lua_pushliteral(L, "__mode"); + lua_pushliteral(L, "k"); + lua_rawset(L, -3); /* metatable(N).__mode = "k" */ + lua_pushvalue(L, -1); + lua_rawseti(L, LUA_REGISTRYINDEX, ARRAYSIZE_REF); /* store in register */ + } +} + + +void luaL_setn (lua_State *L, int t, int n) { + t = abs_index(L, t); + lua_pushliteral(L, "n"); + lua_rawget(L, t); + if (checkint(L, 1) >= 0) { /* is there a numeric field `n'? */ + lua_pushliteral(L, "n"); /* use it */ + lua_pushnumber(L, (lua_Number)n); + lua_rawset(L, t); + } + else { /* use `sizes' */ + getsizes(L); + lua_pushvalue(L, t); + lua_pushnumber(L, (lua_Number)n); + lua_rawset(L, -3); /* sizes[t] = n */ + lua_pop(L, 1); /* remove `sizes' */ + } +} + + +int luaL_getn (lua_State *L, int t) { + int n; + t = abs_index(L, t); + lua_pushliteral(L, "n"); /* try t.n */ + lua_rawget(L, t); + if ((n = checkint(L, 1)) >= 0) return n; + getsizes(L); /* else try sizes[t] */ + lua_pushvalue(L, t); + lua_rawget(L, -2); + if ((n = checkint(L, 2)) >= 0) return n; + for (n = 1; ; n++) { /* else must count elements */ + lua_rawgeti(L, t, n); + if (lua_isnil(L, -1)) break; + lua_pop(L, 1); + } + lua_pop(L, 1); + return n - 1; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + + +#define bufflen(B) ((B)->p - (B)->buffer) +#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B))) + +#define LIMIT (LUA_MINSTACK/2) + + +static int emptybuffer (luaL_Buffer *B) { + size_t l = bufflen(B); + if (l == 0) return 0; /* put nothing on stack */ + else { + lua_pushlstring(B->L, B->buffer, l); + B->p = B->buffer; + B->lvl++; + return 1; + } +} + + +static void adjuststack (luaL_Buffer *B) { + if (B->lvl > 1) { + lua_State *L = B->L; + int toget = 1; /* number of levels to concat */ + size_t toplen = lua_strlen(L, -1); + do { + size_t l = lua_strlen(L, -(toget+1)); + if (B->lvl - toget + 1 >= LIMIT || toplen > l) { + toplen += l; + toget++; + } + else break; + } while (toget < B->lvl); + lua_concat(L, toget); + B->lvl = B->lvl - toget + 1; + } +} + + +LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) { + if (emptybuffer(B)) + adjuststack(B); + return B->buffer; +} + + +LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { + while (l--) + luaL_putchar(B, *s++); +} + + +LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { + luaL_addlstring(B, s, strlen(s)); +} + + +LUALIB_API void luaL_pushresult (luaL_Buffer *B) { + emptybuffer(B); + lua_concat(B->L, B->lvl); + B->lvl = 1; +} + + +LUALIB_API void luaL_addvalue (luaL_Buffer *B) { + lua_State *L = B->L; + size_t vl = lua_strlen(L, -1); + if (vl <= bufffree(B)) { /* fit into buffer? */ + memcpy(B->p, lua_tostring(L, -1), vl); /* put it there */ + B->p += vl; + lua_pop(L, 1); /* remove from stack */ + } + else { + if (emptybuffer(B)) + lua_insert(L, -2); /* put buffer before new value */ + B->lvl++; /* add new value into B stack */ + adjuststack(B); + } +} + + +LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { + B->L = L; + B->p = B->buffer; + B->lvl = 0; +} + +/* }====================================================== */ + + +LUALIB_API int luaL_ref (lua_State *L, int t) { + int ref; + t = abs_index(L, t); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* remove from stack */ + return LUA_REFNIL; /* `nil' has a unique fixed reference */ + } + lua_rawgeti(L, t, FREELIST_REF); /* get first free element */ + ref = (int)lua_tonumber(L, -1); /* ref = t[FREELIST_REF] */ + lua_pop(L, 1); /* remove it from stack */ + if (ref != 0) { /* any free element? */ + lua_rawgeti(L, t, ref); /* remove it from list */ + lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */ + } + else { /* no free elements */ + ref = luaL_getn(L, t); + if (ref < RESERVED_REFS) + ref = RESERVED_REFS; /* skip reserved references */ + ref++; /* create new reference */ + luaL_setn(L, t, ref); + } + lua_rawseti(L, t, ref); + return ref; +} + + +LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { + if (ref >= 0) { + t = abs_index(L, t); + lua_rawgeti(L, t, FREELIST_REF); + lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */ + lua_pushnumber(L, (lua_Number)ref); + lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */ + } +} + + + +/* +** {====================================================== +** Load functions +** ======================================================= +*/ + +typedef struct LoadF { + FILE *f; + char buff[LUAL_BUFFERSIZE]; +} LoadF; + + +static const char *getF (lua_State *L, void *ud, size_t *size) { + LoadF *lf = (LoadF *)ud; + (void)L; + if (feof(lf->f)) return NULL; + *size = fread(lf->buff, 1, LUAL_BUFFERSIZE, lf->f); + return (*size > 0) ? lf->buff : NULL; +} + + +static int errfile (lua_State *L, int fnameindex) { + const char *filename = lua_tostring(L, fnameindex) + 1; + lua_pushfstring(L, "cannot read %s: %s", filename, strerror(errno)); + lua_remove(L, fnameindex); + return LUA_ERRFILE; +} + + +LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) { + LoadF lf; + int status, readstatus; + int c; + int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ + if (filename == NULL) { + lua_pushliteral(L, "=stdin"); + lf.f = stdin; + } + else { + lua_pushfstring(L, "@%s", filename); + lf.f = fopen(filename, "r"); + } + if (lf.f == NULL) return errfile(L, fnameindex); /* unable to open file */ + c = ungetc(getc(lf.f), lf.f); + if (!(isspace(c) || isprint(c)) && lf.f != stdin) { /* binary file? */ + fclose(lf.f); + lf.f = fopen(filename, "rb"); /* reopen in binary mode */ + if (lf.f == NULL) return errfile(L, fnameindex); /* unable to reopen file */ + } + status = lua_load(L, getF, &lf, lua_tostring(L, -1)); + readstatus = ferror(lf.f); + if (lf.f != stdin) fclose(lf.f); /* close file (even in case of errors) */ + if (readstatus) { + lua_settop(L, fnameindex); /* ignore results from `lua_load' */ + return errfile(L, fnameindex); + } + lua_remove(L, fnameindex); + return status; +} + + +typedef struct LoadS { + const char *s; + size_t size; +} LoadS; + + +static const char *getS (lua_State *L, void *ud, size_t *size) { + LoadS *ls = (LoadS *)ud; + (void)L; + if (ls->size == 0) return NULL; + *size = ls->size; + ls->size = 0; + return ls->s; +} + + +LUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size, + const char *name) { + LoadS ls; + ls.s = buff; + ls.size = size; + return lua_load(L, getS, &ls, name); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** compatibility code +** ======================================================= +*/ + + +static void callalert (lua_State *L, int status) { + if (status != 0) { + lua_getglobal(L, "_ALERT"); + if (lua_isfunction(L, -1)) { + lua_insert(L, -2); + lua_call(L, 1, 0); + } + else { /* no _ALERT function; print it on stderr */ + fprintf(stderr, "%s\n", lua_tostring(L, -2)); + lua_pop(L, 2); /* remove error message and _ALERT */ + } + } +} + + +static int aux_do (lua_State *L, int status) { + if (status == 0) { /* parse OK? */ + status = lua_pcall(L, 0, LUA_MULTRET, 0); /* call main */ + } + callalert(L, status); + return status; +} + + +LUALIB_API int lua_dofile (lua_State *L, const char *filename) { + return aux_do(L, luaL_loadfile(L, filename)); +} + + +LUALIB_API int lua_dobuffer (lua_State *L, const char *buff, size_t size, + const char *name) { + return aux_do(L, luaL_loadbuffer(L, buff, size, name)); +} + + +LUALIB_API int lua_dostring (lua_State *L, const char *str) { + return lua_dobuffer(L, str, strlen(str), str); +} + +/* }====================================================== */ diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lbaselib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lbaselib.c index 1b320924d2..9f613bcdb9 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lbaselib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lbaselib.c @@ -1,684 +1,684 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - - -#include -#include -#include -#include - -#define lbaselib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - - - -/* -** If your system does not support `stdout', you can just remove this function. -** If you need, you can define your own `print' function, following this -** model but changing `fputs' to put the strings at a proper place -** (a console window or a log file, for instance). -*/ -static int luaB_print (lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - int i; - lua_getglobal(L, "tostring"); - for (i=1; i<=n; i++) { - const char *s; - lua_pushvalue(L, -1); /* function to be called */ - lua_pushvalue(L, i); /* value to print */ - lua_call(L, 1, 1); - s = lua_tostring(L, -1); /* get result */ - if (s == NULL) - return luaL_error(L, "`tostring' must return a string to `print'"); - if (i>1) fputs("\t", stdout); - fputs(s, stdout); - lua_pop(L, 1); /* pop result */ - } - fputs("\n", stdout); - return 0; -} - - -static int luaB_tonumber (lua_State *L) { - int base = luaL_optint(L, 2, 10); - if (base == 10) { /* standard conversion */ - luaL_checkany(L, 1); - if (lua_isnumber(L, 1)) { - lua_pushnumber(L, lua_tonumber(L, 1)); - return 1; - } - } - else { - const char *s1 = luaL_checkstring(L, 1); - char *s2; - unsigned long n; - luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); - n = strtoul(s1, &s2, base); - if (s1 != s2) { /* at least one valid digit? */ - while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */ - if (*s2 == '\0') { /* no invalid trailing characters? */ - lua_pushnumber(L, (lua_Number)n); - return 1; - } - } - } - lua_pushnil(L); /* else not a number */ - return 1; -} - - -static int luaB_error (lua_State *L) { - int level = luaL_optint(L, 2, 1); - luaL_checkany(L, 1); - if (!lua_isstring(L, 1) || level == 0) - lua_pushvalue(L, 1); /* propagate error message without changes */ - else { /* add extra information */ - luaL_where(L, level); - lua_pushvalue(L, 1); - lua_concat(L, 2); - } - return lua_error(L); -} - - -static int luaB_getmetatable (lua_State *L) { - luaL_checkany(L, 1); - if (!lua_getmetatable(L, 1)) { - lua_pushnil(L); - return 1; /* no metatable */ - } - luaL_getmetafield(L, 1, "__metatable"); - return 1; /* returns either __metatable field (if present) or metatable */ -} - - -static int luaB_setmetatable (lua_State *L) { - int t = lua_type(L, 2); - luaL_checktype(L, 1, LUA_TTABLE); - luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, - "nil or table expected"); - if (luaL_getmetafield(L, 1, "__metatable")) - luaL_error(L, "cannot change a protected metatable"); - lua_settop(L, 2); - lua_setmetatable(L, 1); - return 1; -} - - -static void getfunc (lua_State *L) { - if (lua_isfunction(L, 1)) lua_pushvalue(L, 1); - else { - lua_Debug ar; - int level = luaL_optint(L, 1, 1); - luaL_argcheck(L, level >= 0, 1, "level must be non-negative"); - if (lua_getstack(L, level, &ar) == 0) - luaL_argerror(L, 1, "invalid level"); - lua_getinfo(L, "f", &ar); - if (lua_isnil(L, -1)) - luaL_error(L, "no function environment for tail call at level %d", - level); - } -} - - -static int aux_getfenv (lua_State *L) { - lua_getfenv(L, -1); - lua_pushliteral(L, "__fenv"); - lua_rawget(L, -2); - return !lua_isnil(L, -1); -} - - -static int luaB_getfenv (lua_State *L) { - getfunc(L); - if (!aux_getfenv(L)) /* __fenv not defined? */ - lua_pop(L, 1); /* remove it, to return real environment */ - return 1; -} - - -static int luaB_setfenv (lua_State *L) { - luaL_checktype(L, 2, LUA_TTABLE); - getfunc(L); - if (aux_getfenv(L)) /* __fenv defined? */ - luaL_error(L, "`setfenv' cannot change a protected environment"); - else - lua_pop(L, 2); /* remove __fenv and real environment table */ - lua_pushvalue(L, 2); - if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) - lua_replace(L, LUA_GLOBALSINDEX); - else if (lua_setfenv(L, -2) == 0) - luaL_error(L, "`setfenv' cannot change environment of given function"); - return 0; -} - - -static int luaB_rawequal (lua_State *L) { - luaL_checkany(L, 1); - luaL_checkany(L, 2); - lua_pushboolean(L, lua_rawequal(L, 1, 2)); - return 1; -} - - -static int luaB_rawget (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - luaL_checkany(L, 2); - lua_rawget(L, 1); - return 1; -} - -static int luaB_rawset (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - luaL_checkany(L, 2); - luaL_checkany(L, 3); - lua_rawset(L, 1); - return 1; -} - - -static int luaB_gcinfo (lua_State *L) { - lua_pushnumber(L, (lua_Number)lua_getgccount(L)); - lua_pushnumber(L, (lua_Number)lua_getgcthreshold(L)); - return 2; -} - - -static int luaB_collectgarbage (lua_State *L) { - lua_setgcthreshold(L, luaL_optint(L, 1, 0)); - return 0; -} - - -static int luaB_type (lua_State *L) { - luaL_checkany(L, 1); - lua_pushstring(L, lua_typename(L, lua_type(L, 1))); - return 1; -} - - -static int luaB_next (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - lua_settop(L, 2); /* create a 2nd argument if there isn't one */ - if (lua_next(L, 1)) - return 2; - else { - lua_pushnil(L); - return 1; - } -} - - -static int luaB_pairs (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - lua_pushliteral(L, "next"); - lua_rawget(L, LUA_GLOBALSINDEX); /* return generator, */ - lua_pushvalue(L, 1); /* state, */ - lua_pushnil(L); /* and initial value */ - return 3; -} - - -static int luaB_ipairs (lua_State *L) { - lua_Number i = lua_tonumber(L, 2); - luaL_checktype(L, 1, LUA_TTABLE); - if (i == 0 && lua_isnone(L, 2)) { /* `for' start? */ - lua_pushliteral(L, "ipairs"); - lua_rawget(L, LUA_GLOBALSINDEX); /* return generator, */ - lua_pushvalue(L, 1); /* state, */ - lua_pushnumber(L, 0); /* and initial value */ - return 3; - } - else { /* `for' step */ - i++; /* next value */ - lua_pushnumber(L, i); - lua_rawgeti(L, 1, (int)i); - return (lua_isnil(L, -1)) ? 0 : 2; - } -} - - -static int load_aux (lua_State *L, int status) { - if (status == 0) /* OK? */ - return 1; - else { - lua_pushnil(L); - lua_insert(L, -2); /* put before error message */ - return 2; /* return nil plus error message */ - } -} - - -static int luaB_loadstring (lua_State *L) { - size_t l; - const char *s = luaL_checklstring(L, 1, &l); - const char *chunkname = luaL_optstring(L, 2, s); - return load_aux(L, luaL_loadbuffer(L, s, l, chunkname)); -} - - -static int luaB_loadfile (lua_State *L) { - const char *fname = luaL_optstring(L, 1, NULL); - return load_aux(L, luaL_loadfile(L, fname)); -} - - -static int luaB_dofile (lua_State *L) { - const char *fname = luaL_optstring(L, 1, NULL); - int status = luaL_loadfile(L, fname); - if (status != 0) lua_error(L); - lua_call(L, 0, LUA_MULTRET); - return lua_gettop(L) - 1; -} - - -static int luaB_assert (lua_State *L) { - luaL_checkany(L, 1); - if (!lua_toboolean(L, 1)) - return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!")); - lua_settop(L, 1); - return 1; -} - - -static int luaB_unpack (lua_State *L) { - int n, i; - luaL_checktype(L, 1, LUA_TTABLE); - n = luaL_getn(L, 1); - luaL_checkstack(L, n, "table too big to unpack"); - for (i=1; i<=n; i++) /* push arg[1...n] */ - lua_rawgeti(L, 1, i); - return n; -} - - -static int luaB_pcall (lua_State *L) { - int status; - luaL_checkany(L, 1); - status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); - lua_pushboolean(L, (status == 0)); - lua_insert(L, 1); - return lua_gettop(L); /* return status + all results */ -} - - -static int luaB_xpcall (lua_State *L) { - int status; - luaL_checkany(L, 2); - lua_settop(L, 2); - lua_insert(L, 1); /* put error function under function to be called */ - status = lua_pcall(L, 0, LUA_MULTRET, 1); - lua_pushboolean(L, (status == 0)); - lua_replace(L, 1); - return lua_gettop(L); /* return status + all results */ -} - - -static int luaB_tostring (lua_State *L) { - char buff[64]; - luaL_checkany(L, 1); - if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */ - return 1; /* use its value */ - switch (lua_type(L, 1)) { - case LUA_TNUMBER: - lua_pushstring(L, lua_tostring(L, 1)); - return 1; - case LUA_TSTRING: - lua_pushvalue(L, 1); - return 1; - case LUA_TBOOLEAN: - lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false")); - return 1; - case LUA_TTABLE: - sprintf(buff, "table: %p", lua_topointer(L, 1)); - break; - case LUA_TFUNCTION: - sprintf(buff, "function: %p", lua_topointer(L, 1)); - break; - case LUA_TUSERDATA: - case LUA_TLIGHTUSERDATA: - sprintf(buff, "userdata: %p", lua_touserdata(L, 1)); - break; - case LUA_TTHREAD: - sprintf(buff, "thread: %p", (void *)lua_tothread(L, 1)); - break; - case LUA_TNIL: - lua_pushliteral(L, "nil"); - return 1; - } - lua_pushstring(L, buff); - return 1; -} - - -static int luaB_newproxy (lua_State *L) { - lua_settop(L, 1); - lua_newuserdata(L, 0); /* create proxy */ - if (lua_toboolean(L, 1) == 0) - return 1; /* no metatable */ - else if (lua_isboolean(L, 1)) { - lua_newtable(L); /* create a new metatable `m' ... */ - lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */ - lua_pushboolean(L, 1); - lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */ - } - else { - int validproxy = 0; /* to check if weaktable[metatable(u)] == true */ - if (lua_getmetatable(L, 1)) { - lua_rawget(L, lua_upvalueindex(1)); - validproxy = lua_toboolean(L, -1); - lua_pop(L, 1); /* remove value */ - } - luaL_argcheck(L, validproxy, 1, "boolean or proxy expected"); - lua_getmetatable(L, 1); /* metatable is valid; get it */ - } - lua_setmetatable(L, 2); - return 1; -} - - -/* -** {====================================================== -** `require' function -** ======================================================= -*/ - - -/* name of global that holds table with loaded packages */ -#define REQTAB "_LOADED" - -/* name of global that holds the search path for packages */ -#define LUA_PATH "LUA_PATH" - -#ifndef LUA_PATH_SEP -#define LUA_PATH_SEP ';' -#endif - -#ifndef LUA_PATH_MARK -#define LUA_PATH_MARK '?' -#endif - -#ifndef LUA_PATH_DEFAULT -#define LUA_PATH_DEFAULT "?;?.lua" -#endif - - -static const char *getpath (lua_State *L) { - const char *path; - lua_getglobal(L, LUA_PATH); /* try global variable */ - path = lua_tostring(L, -1); - lua_pop(L, 1); - if (path) return path; - path = getenv(LUA_PATH); /* else try environment variable */ - if (path) return path; - return LUA_PATH_DEFAULT; /* else use default */ -} - - -static const char *pushnextpath (lua_State *L, const char *path) { - const char *l; - if (*path == '\0') return NULL; /* no more paths */ - if (*path == LUA_PATH_SEP) path++; /* skip separator */ - l = strchr(path, LUA_PATH_SEP); /* find next separator */ - if (l == NULL) l = path+strlen(path); - lua_pushlstring(L, path, l - path); /* directory name */ - return l; -} - - -static void pushcomposename (lua_State *L) { - const char *path = lua_tostring(L, -1); - const char *wild; - int n = 1; - while ((wild = strchr(path, LUA_PATH_MARK)) != NULL) { - /* is there stack space for prefix, name, and eventual last sufix? */ - luaL_checkstack(L, 3, "too many marks in a path component"); - lua_pushlstring(L, path, wild - path); /* push prefix */ - lua_pushvalue(L, 1); /* push package name (in place of MARK) */ - path = wild + 1; /* continue after MARK */ - n += 2; - } - lua_pushstring(L, path); /* push last sufix (`n' already includes this) */ - lua_concat(L, n); -} - - -static int luaB_require (lua_State *L) { - const char *path; - int status = LUA_ERRFILE; /* not found (yet) */ - luaL_checkstring(L, 1); - lua_settop(L, 1); - lua_getglobal(L, REQTAB); - if (!lua_istable(L, 2)) return luaL_error(L, "`" REQTAB "' is not a table"); - path = getpath(L); - lua_pushvalue(L, 1); /* check package's name in book-keeping table */ - lua_rawget(L, 2); - if (lua_toboolean(L, -1)) /* is it there? */ - return 1; /* package is already loaded; return its result */ - else { /* must load it */ - while (status == LUA_ERRFILE) { - lua_settop(L, 3); /* reset stack position */ - if ((path = pushnextpath(L, path)) == NULL) break; - pushcomposename(L); - status = luaL_loadfile(L, lua_tostring(L, -1)); /* try to load it */ - } - } - switch (status) { - case 0: { - lua_getglobal(L, "_REQUIREDNAME"); /* save previous name */ - lua_insert(L, -2); /* put it below function */ - lua_pushvalue(L, 1); - lua_setglobal(L, "_REQUIREDNAME"); /* set new name */ - lua_call(L, 0, 1); /* run loaded module */ - lua_insert(L, -2); /* put result below previous name */ - lua_setglobal(L, "_REQUIREDNAME"); /* reset to previous name */ - if (lua_isnil(L, -1)) { /* no/nil return? */ - lua_pushboolean(L, 1); - lua_replace(L, -2); /* replace to true */ - } - lua_pushvalue(L, 1); - lua_pushvalue(L, -2); - lua_rawset(L, 2); /* mark it as loaded */ - return 1; /* return value */ - } - case LUA_ERRFILE: { /* file not found */ - return luaL_error(L, "could not load package `%s' from path `%s'", - lua_tostring(L, 1), getpath(L)); - } - default: { - return luaL_error(L, "error loading package `%s' (%s)", - lua_tostring(L, 1), lua_tostring(L, -1)); - } - } -} - -/* }====================================================== */ - - -static const luaL_reg base_funcs[] = { - {"error", luaB_error}, - {"getmetatable", luaB_getmetatable}, - {"setmetatable", luaB_setmetatable}, - {"getfenv", luaB_getfenv}, - {"setfenv", luaB_setfenv}, - {"next", luaB_next}, - {"ipairs", luaB_ipairs}, - {"pairs", luaB_pairs}, - {"print", luaB_print}, - {"tonumber", luaB_tonumber}, - {"tostring", luaB_tostring}, - {"type", luaB_type}, - {"assert", luaB_assert}, - {"unpack", luaB_unpack}, - {"rawequal", luaB_rawequal}, - {"rawget", luaB_rawget}, - {"rawset", luaB_rawset}, - {"pcall", luaB_pcall}, - {"xpcall", luaB_xpcall}, - {"collectgarbage", luaB_collectgarbage}, - {"gcinfo", luaB_gcinfo}, - {"loadfile", luaB_loadfile}, - {"dofile", luaB_dofile}, - {"loadstring", luaB_loadstring}, - {"require", luaB_require}, - {NULL, NULL} -}; - - -/* -** {====================================================== -** Coroutine library -** ======================================================= -*/ - -static int auxresume (lua_State *L, lua_State *co, int narg) { - int status; - if (!lua_checkstack(co, narg)) - luaL_error(L, "too many arguments to resume"); - lua_xmove(L, co, narg); - status = lua_resume(co, narg); - if (status == 0) { - int nres = lua_gettop(co); - if (!lua_checkstack(L, nres)) - luaL_error(L, "too many results to resume"); - lua_xmove(co, L, nres); /* move yielded values */ - return nres; - } - else { - lua_xmove(co, L, 1); /* move error message */ - return -1; /* error flag */ - } -} - - -static int luaB_coresume (lua_State *L) { - lua_State *co = lua_tothread(L, 1); - int r; - luaL_argcheck(L, co, 1, "coroutine expected"); - r = auxresume(L, co, lua_gettop(L) - 1); - if (r < 0) { - lua_pushboolean(L, 0); - lua_insert(L, -2); - return 2; /* return false + error message */ - } - else { - lua_pushboolean(L, 1); - lua_insert(L, -(r + 1)); - return r + 1; /* return true + `resume' returns */ - } -} - - -static int luaB_auxwrap (lua_State *L) { - lua_State *co = lua_tothread(L, lua_upvalueindex(1)); - int r = auxresume(L, co, lua_gettop(L)); - if (r < 0) { - if (lua_isstring(L, -1)) { /* error object is a string? */ - luaL_where(L, 1); /* add extra info */ - lua_insert(L, -2); - lua_concat(L, 2); - } - lua_error(L); /* propagate error */ - } - return r; -} - - -static int luaB_cocreate (lua_State *L) { - lua_State *NL = lua_newthread(L); - luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, - "Lua function expected"); - lua_pushvalue(L, 1); /* move function to top */ - lua_xmove(L, NL, 1); /* move function from L to NL */ - return 1; -} - - -static int luaB_cowrap (lua_State *L) { - luaB_cocreate(L); - lua_pushcclosure(L, luaB_auxwrap, 1); - return 1; -} - - -static int luaB_yield (lua_State *L) { - return lua_yield(L, lua_gettop(L)); -} - - -static int luaB_costatus (lua_State *L) { - lua_State *co = lua_tothread(L, 1); - luaL_argcheck(L, co, 1, "coroutine expected"); - if (L == co) lua_pushliteral(L, "running"); - else { - lua_Debug ar; - if (lua_getstack(co, 0, &ar) == 0 && lua_gettop(co) == 0) - lua_pushliteral(L, "dead"); - else - lua_pushliteral(L, "suspended"); - } - return 1; -} - - -static const luaL_reg co_funcs[] = { - {"create", luaB_cocreate}, - {"wrap", luaB_cowrap}, - {"resume", luaB_coresume}, - {"yield", luaB_yield}, - {"status", luaB_costatus}, - {NULL, NULL} -}; - -/* }====================================================== */ - - - -static void base_open (lua_State *L) { - lua_pushliteral(L, "_G"); - lua_pushvalue(L, LUA_GLOBALSINDEX); - luaL_openlib(L, NULL, base_funcs, 0); /* open lib into global table */ - lua_pushliteral(L, "_VERSION"); - lua_pushliteral(L, LUA_VERSION); - lua_rawset(L, -3); /* set global _VERSION */ - /* `newproxy' needs a weaktable as upvalue */ - lua_pushliteral(L, "newproxy"); - lua_newtable(L); /* new table `w' */ - lua_pushvalue(L, -1); /* `w' will be its own metatable */ - lua_setmetatable(L, -2); - lua_pushliteral(L, "__mode"); - lua_pushliteral(L, "k"); - lua_rawset(L, -3); /* metatable(w).__mode = "k" */ - lua_pushcclosure(L, luaB_newproxy, 1); - lua_rawset(L, -3); /* set global `newproxy' */ - lua_rawset(L, -1); /* set global _G */ -} - - -LUALIB_API int luaopen_base (lua_State *L) { - base_open(L); - luaL_openlib(L, LUA_COLIBNAME, co_funcs, 0); - lua_newtable(L); - lua_setglobal(L, REQTAB); - return 0; -} - +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + + +#include +#include +#include +#include + +#define lbaselib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + + +/* +** If your system does not support `stdout', you can just remove this function. +** If you need, you can define your own `print' function, following this +** model but changing `fputs' to put the strings at a proper place +** (a console window or a log file, for instance). +*/ +static int luaB_print (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + lua_getglobal(L, "tostring"); + for (i=1; i<=n; i++) { + const char *s; + lua_pushvalue(L, -1); /* function to be called */ + lua_pushvalue(L, i); /* value to print */ + lua_call(L, 1, 1); + s = lua_tostring(L, -1); /* get result */ + if (s == NULL) + return luaL_error(L, "`tostring' must return a string to `print'"); + if (i>1) fputs("\t", stdout); + fputs(s, stdout); + lua_pop(L, 1); /* pop result */ + } + fputs("\n", stdout); + return 0; +} + + +static int luaB_tonumber (lua_State *L) { + int base = luaL_optint(L, 2, 10); + if (base == 10) { /* standard conversion */ + luaL_checkany(L, 1); + if (lua_isnumber(L, 1)) { + lua_pushnumber(L, lua_tonumber(L, 1)); + return 1; + } + } + else { + const char *s1 = luaL_checkstring(L, 1); + char *s2; + unsigned long n; + luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); + n = strtoul(s1, &s2, base); + if (s1 != s2) { /* at least one valid digit? */ + while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */ + if (*s2 == '\0') { /* no invalid trailing characters? */ + lua_pushnumber(L, (lua_Number)n); + return 1; + } + } + } + lua_pushnil(L); /* else not a number */ + return 1; +} + + +static int luaB_error (lua_State *L) { + int level = luaL_optint(L, 2, 1); + luaL_checkany(L, 1); + if (!lua_isstring(L, 1) || level == 0) + lua_pushvalue(L, 1); /* propagate error message without changes */ + else { /* add extra information */ + luaL_where(L, level); + lua_pushvalue(L, 1); + lua_concat(L, 2); + } + return lua_error(L); +} + + +static int luaB_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); + return 1; /* no metatable */ + } + luaL_getmetafield(L, 1, "__metatable"); + return 1; /* returns either __metatable field (if present) or metatable */ +} + + +static int luaB_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, + "nil or table expected"); + if (luaL_getmetafield(L, 1, "__metatable")) + luaL_error(L, "cannot change a protected metatable"); + lua_settop(L, 2); + lua_setmetatable(L, 1); + return 1; +} + + +static void getfunc (lua_State *L) { + if (lua_isfunction(L, 1)) lua_pushvalue(L, 1); + else { + lua_Debug ar; + int level = luaL_optint(L, 1, 1); + luaL_argcheck(L, level >= 0, 1, "level must be non-negative"); + if (lua_getstack(L, level, &ar) == 0) + luaL_argerror(L, 1, "invalid level"); + lua_getinfo(L, "f", &ar); + if (lua_isnil(L, -1)) + luaL_error(L, "no function environment for tail call at level %d", + level); + } +} + + +static int aux_getfenv (lua_State *L) { + lua_getfenv(L, -1); + lua_pushliteral(L, "__fenv"); + lua_rawget(L, -2); + return !lua_isnil(L, -1); +} + + +static int luaB_getfenv (lua_State *L) { + getfunc(L); + if (!aux_getfenv(L)) /* __fenv not defined? */ + lua_pop(L, 1); /* remove it, to return real environment */ + return 1; +} + + +static int luaB_setfenv (lua_State *L) { + luaL_checktype(L, 2, LUA_TTABLE); + getfunc(L); + if (aux_getfenv(L)) /* __fenv defined? */ + luaL_error(L, "`setfenv' cannot change a protected environment"); + else + lua_pop(L, 2); /* remove __fenv and real environment table */ + lua_pushvalue(L, 2); + if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) + lua_replace(L, LUA_GLOBALSINDEX); + else if (lua_setfenv(L, -2) == 0) + luaL_error(L, "`setfenv' cannot change environment of given function"); + return 0; +} + + +static int luaB_rawequal (lua_State *L) { + luaL_checkany(L, 1); + luaL_checkany(L, 2); + lua_pushboolean(L, lua_rawequal(L, 1, 2)); + return 1; +} + + +static int luaB_rawget (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + lua_rawget(L, 1); + return 1; +} + +static int luaB_rawset (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + luaL_checkany(L, 3); + lua_rawset(L, 1); + return 1; +} + + +static int luaB_gcinfo (lua_State *L) { + lua_pushnumber(L, (lua_Number)lua_getgccount(L)); + lua_pushnumber(L, (lua_Number)lua_getgcthreshold(L)); + return 2; +} + + +static int luaB_collectgarbage (lua_State *L) { + lua_setgcthreshold(L, luaL_optint(L, 1, 0)); + return 0; +} + + +static int luaB_type (lua_State *L) { + luaL_checkany(L, 1); + lua_pushstring(L, lua_typename(L, lua_type(L, 1))); + return 1; +} + + +static int luaB_next (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 2); /* create a 2nd argument if there isn't one */ + if (lua_next(L, 1)) + return 2; + else { + lua_pushnil(L); + return 1; + } +} + + +static int luaB_pairs (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushliteral(L, "next"); + lua_rawget(L, LUA_GLOBALSINDEX); /* return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushnil(L); /* and initial value */ + return 3; +} + + +static int luaB_ipairs (lua_State *L) { + lua_Number i = lua_tonumber(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + if (i == 0 && lua_isnone(L, 2)) { /* `for' start? */ + lua_pushliteral(L, "ipairs"); + lua_rawget(L, LUA_GLOBALSINDEX); /* return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushnumber(L, 0); /* and initial value */ + return 3; + } + else { /* `for' step */ + i++; /* next value */ + lua_pushnumber(L, i); + lua_rawgeti(L, 1, (int)i); + return (lua_isnil(L, -1)) ? 0 : 2; + } +} + + +static int load_aux (lua_State *L, int status) { + if (status == 0) /* OK? */ + return 1; + else { + lua_pushnil(L); + lua_insert(L, -2); /* put before error message */ + return 2; /* return nil plus error message */ + } +} + + +static int luaB_loadstring (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + const char *chunkname = luaL_optstring(L, 2, s); + return load_aux(L, luaL_loadbuffer(L, s, l, chunkname)); +} + + +static int luaB_loadfile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + return load_aux(L, luaL_loadfile(L, fname)); +} + + +static int luaB_dofile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + int status = luaL_loadfile(L, fname); + if (status != 0) lua_error(L); + lua_call(L, 0, LUA_MULTRET); + return lua_gettop(L) - 1; +} + + +static int luaB_assert (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_toboolean(L, 1)) + return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!")); + lua_settop(L, 1); + return 1; +} + + +static int luaB_unpack (lua_State *L) { + int n, i; + luaL_checktype(L, 1, LUA_TTABLE); + n = luaL_getn(L, 1); + luaL_checkstack(L, n, "table too big to unpack"); + for (i=1; i<=n; i++) /* push arg[1...n] */ + lua_rawgeti(L, 1, i); + return n; +} + + +static int luaB_pcall (lua_State *L) { + int status; + luaL_checkany(L, 1); + status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); + lua_pushboolean(L, (status == 0)); + lua_insert(L, 1); + return lua_gettop(L); /* return status + all results */ +} + + +static int luaB_xpcall (lua_State *L) { + int status; + luaL_checkany(L, 2); + lua_settop(L, 2); + lua_insert(L, 1); /* put error function under function to be called */ + status = lua_pcall(L, 0, LUA_MULTRET, 1); + lua_pushboolean(L, (status == 0)); + lua_replace(L, 1); + return lua_gettop(L); /* return status + all results */ +} + + +static int luaB_tostring (lua_State *L) { + char buff[64]; + luaL_checkany(L, 1); + if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */ + return 1; /* use its value */ + switch (lua_type(L, 1)) { + case LUA_TNUMBER: + lua_pushstring(L, lua_tostring(L, 1)); + return 1; + case LUA_TSTRING: + lua_pushvalue(L, 1); + return 1; + case LUA_TBOOLEAN: + lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false")); + return 1; + case LUA_TTABLE: + sprintf(buff, "table: %p", lua_topointer(L, 1)); + break; + case LUA_TFUNCTION: + sprintf(buff, "function: %p", lua_topointer(L, 1)); + break; + case LUA_TUSERDATA: + case LUA_TLIGHTUSERDATA: + sprintf(buff, "userdata: %p", lua_touserdata(L, 1)); + break; + case LUA_TTHREAD: + sprintf(buff, "thread: %p", (void *)lua_tothread(L, 1)); + break; + case LUA_TNIL: + lua_pushliteral(L, "nil"); + return 1; + } + lua_pushstring(L, buff); + return 1; +} + + +static int luaB_newproxy (lua_State *L) { + lua_settop(L, 1); + lua_newuserdata(L, 0); /* create proxy */ + if (lua_toboolean(L, 1) == 0) + return 1; /* no metatable */ + else if (lua_isboolean(L, 1)) { + lua_newtable(L); /* create a new metatable `m' ... */ + lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */ + lua_pushboolean(L, 1); + lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */ + } + else { + int validproxy = 0; /* to check if weaktable[metatable(u)] == true */ + if (lua_getmetatable(L, 1)) { + lua_rawget(L, lua_upvalueindex(1)); + validproxy = lua_toboolean(L, -1); + lua_pop(L, 1); /* remove value */ + } + luaL_argcheck(L, validproxy, 1, "boolean or proxy expected"); + lua_getmetatable(L, 1); /* metatable is valid; get it */ + } + lua_setmetatable(L, 2); + return 1; +} + + +/* +** {====================================================== +** `require' function +** ======================================================= +*/ + + +/* name of global that holds table with loaded packages */ +#define REQTAB "_LOADED" + +/* name of global that holds the search path for packages */ +#define LUA_PATH "LUA_PATH" + +#ifndef LUA_PATH_SEP +#define LUA_PATH_SEP ';' +#endif + +#ifndef LUA_PATH_MARK +#define LUA_PATH_MARK '?' +#endif + +#ifndef LUA_PATH_DEFAULT +#define LUA_PATH_DEFAULT "?;?.lua" +#endif + + +static const char *getpath (lua_State *L) { + const char *path; + lua_getglobal(L, LUA_PATH); /* try global variable */ + path = lua_tostring(L, -1); + lua_pop(L, 1); + if (path) return path; + path = getenv(LUA_PATH); /* else try environment variable */ + if (path) return path; + return LUA_PATH_DEFAULT; /* else use default */ +} + + +static const char *pushnextpath (lua_State *L, const char *path) { + const char *l; + if (*path == '\0') return NULL; /* no more paths */ + if (*path == LUA_PATH_SEP) path++; /* skip separator */ + l = strchr(path, LUA_PATH_SEP); /* find next separator */ + if (l == NULL) l = path+strlen(path); + lua_pushlstring(L, path, l - path); /* directory name */ + return l; +} + + +static void pushcomposename (lua_State *L) { + const char *path = lua_tostring(L, -1); + const char *wild; + int n = 1; + while ((wild = strchr(path, LUA_PATH_MARK)) != NULL) { + /* is there stack space for prefix, name, and eventual last sufix? */ + luaL_checkstack(L, 3, "too many marks in a path component"); + lua_pushlstring(L, path, wild - path); /* push prefix */ + lua_pushvalue(L, 1); /* push package name (in place of MARK) */ + path = wild + 1; /* continue after MARK */ + n += 2; + } + lua_pushstring(L, path); /* push last sufix (`n' already includes this) */ + lua_concat(L, n); +} + + +static int luaB_require (lua_State *L) { + const char *path; + int status = LUA_ERRFILE; /* not found (yet) */ + luaL_checkstring(L, 1); + lua_settop(L, 1); + lua_getglobal(L, REQTAB); + if (!lua_istable(L, 2)) return luaL_error(L, "`" REQTAB "' is not a table"); + path = getpath(L); + lua_pushvalue(L, 1); /* check package's name in book-keeping table */ + lua_rawget(L, 2); + if (lua_toboolean(L, -1)) /* is it there? */ + return 1; /* package is already loaded; return its result */ + else { /* must load it */ + while (status == LUA_ERRFILE) { + lua_settop(L, 3); /* reset stack position */ + if ((path = pushnextpath(L, path)) == NULL) break; + pushcomposename(L); + status = luaL_loadfile(L, lua_tostring(L, -1)); /* try to load it */ + } + } + switch (status) { + case 0: { + lua_getglobal(L, "_REQUIREDNAME"); /* save previous name */ + lua_insert(L, -2); /* put it below function */ + lua_pushvalue(L, 1); + lua_setglobal(L, "_REQUIREDNAME"); /* set new name */ + lua_call(L, 0, 1); /* run loaded module */ + lua_insert(L, -2); /* put result below previous name */ + lua_setglobal(L, "_REQUIREDNAME"); /* reset to previous name */ + if (lua_isnil(L, -1)) { /* no/nil return? */ + lua_pushboolean(L, 1); + lua_replace(L, -2); /* replace to true */ + } + lua_pushvalue(L, 1); + lua_pushvalue(L, -2); + lua_rawset(L, 2); /* mark it as loaded */ + return 1; /* return value */ + } + case LUA_ERRFILE: { /* file not found */ + return luaL_error(L, "could not load package `%s' from path `%s'", + lua_tostring(L, 1), getpath(L)); + } + default: { + return luaL_error(L, "error loading package `%s' (%s)", + lua_tostring(L, 1), lua_tostring(L, -1)); + } + } +} + +/* }====================================================== */ + + +static const luaL_reg base_funcs[] = { + {"error", luaB_error}, + {"getmetatable", luaB_getmetatable}, + {"setmetatable", luaB_setmetatable}, + {"getfenv", luaB_getfenv}, + {"setfenv", luaB_setfenv}, + {"next", luaB_next}, + {"ipairs", luaB_ipairs}, + {"pairs", luaB_pairs}, + {"print", luaB_print}, + {"tonumber", luaB_tonumber}, + {"tostring", luaB_tostring}, + {"type", luaB_type}, + {"assert", luaB_assert}, + {"unpack", luaB_unpack}, + {"rawequal", luaB_rawequal}, + {"rawget", luaB_rawget}, + {"rawset", luaB_rawset}, + {"pcall", luaB_pcall}, + {"xpcall", luaB_xpcall}, + {"collectgarbage", luaB_collectgarbage}, + {"gcinfo", luaB_gcinfo}, + {"loadfile", luaB_loadfile}, + {"dofile", luaB_dofile}, + {"loadstring", luaB_loadstring}, + {"require", luaB_require}, + {NULL, NULL} +}; + + +/* +** {====================================================== +** Coroutine library +** ======================================================= +*/ + +static int auxresume (lua_State *L, lua_State *co, int narg) { + int status; + if (!lua_checkstack(co, narg)) + luaL_error(L, "too many arguments to resume"); + lua_xmove(L, co, narg); + status = lua_resume(co, narg); + if (status == 0) { + int nres = lua_gettop(co); + if (!lua_checkstack(L, nres)) + luaL_error(L, "too many results to resume"); + lua_xmove(co, L, nres); /* move yielded values */ + return nres; + } + else { + lua_xmove(co, L, 1); /* move error message */ + return -1; /* error flag */ + } +} + + +static int luaB_coresume (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + int r; + luaL_argcheck(L, co, 1, "coroutine expected"); + r = auxresume(L, co, lua_gettop(L) - 1); + if (r < 0) { + lua_pushboolean(L, 0); + lua_insert(L, -2); + return 2; /* return false + error message */ + } + else { + lua_pushboolean(L, 1); + lua_insert(L, -(r + 1)); + return r + 1; /* return true + `resume' returns */ + } +} + + +static int luaB_auxwrap (lua_State *L) { + lua_State *co = lua_tothread(L, lua_upvalueindex(1)); + int r = auxresume(L, co, lua_gettop(L)); + if (r < 0) { + if (lua_isstring(L, -1)) { /* error object is a string? */ + luaL_where(L, 1); /* add extra info */ + lua_insert(L, -2); + lua_concat(L, 2); + } + lua_error(L); /* propagate error */ + } + return r; +} + + +static int luaB_cocreate (lua_State *L) { + lua_State *NL = lua_newthread(L); + luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, + "Lua function expected"); + lua_pushvalue(L, 1); /* move function to top */ + lua_xmove(L, NL, 1); /* move function from L to NL */ + return 1; +} + + +static int luaB_cowrap (lua_State *L) { + luaB_cocreate(L); + lua_pushcclosure(L, luaB_auxwrap, 1); + return 1; +} + + +static int luaB_yield (lua_State *L) { + return lua_yield(L, lua_gettop(L)); +} + + +static int luaB_costatus (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + luaL_argcheck(L, co, 1, "coroutine expected"); + if (L == co) lua_pushliteral(L, "running"); + else { + lua_Debug ar; + if (lua_getstack(co, 0, &ar) == 0 && lua_gettop(co) == 0) + lua_pushliteral(L, "dead"); + else + lua_pushliteral(L, "suspended"); + } + return 1; +} + + +static const luaL_reg co_funcs[] = { + {"create", luaB_cocreate}, + {"wrap", luaB_cowrap}, + {"resume", luaB_coresume}, + {"yield", luaB_yield}, + {"status", luaB_costatus}, + {NULL, NULL} +}; + +/* }====================================================== */ + + + +static void base_open (lua_State *L) { + lua_pushliteral(L, "_G"); + lua_pushvalue(L, LUA_GLOBALSINDEX); + luaL_openlib(L, NULL, base_funcs, 0); /* open lib into global table */ + lua_pushliteral(L, "_VERSION"); + lua_pushliteral(L, LUA_VERSION); + lua_rawset(L, -3); /* set global _VERSION */ + /* `newproxy' needs a weaktable as upvalue */ + lua_pushliteral(L, "newproxy"); + lua_newtable(L); /* new table `w' */ + lua_pushvalue(L, -1); /* `w' will be its own metatable */ + lua_setmetatable(L, -2); + lua_pushliteral(L, "__mode"); + lua_pushliteral(L, "k"); + lua_rawset(L, -3); /* metatable(w).__mode = "k" */ + lua_pushcclosure(L, luaB_newproxy, 1); + lua_rawset(L, -3); /* set global `newproxy' */ + lua_rawset(L, -1); /* set global _G */ +} + + +LUALIB_API int luaopen_base (lua_State *L) { + base_open(L); + luaL_openlib(L, LUA_COLIBNAME, co_funcs, 0); + lua_newtable(L); + lua_setglobal(L, REQTAB); + return 0; +} + diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ldblib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ldblib.c index 55d4d042c7..8424a37847 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ldblib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ldblib.c @@ -1,309 +1,309 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include -#include -#include - -#define ldblib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - - -static void settabss (lua_State *L, const char *i, const char *v) { - lua_pushstring(L, i); - lua_pushstring(L, v); - lua_rawset(L, -3); -} - - -static void settabsi (lua_State *L, const char *i, int v) { - lua_pushstring(L, i); - lua_pushnumber(L, (lua_Number)v); - lua_rawset(L, -3); -} - - -static int getinfo (lua_State *L) { - lua_Debug ar; - const char *options = luaL_optstring(L, 2, "flnSu"); - if (lua_isnumber(L, 1)) { - if (!lua_getstack(L, (int)(lua_tonumber(L, 1)), &ar)) { - lua_pushnil(L); /* level out of range */ - return 1; - } - } - else if (lua_isfunction(L, 1)) { - lua_pushfstring(L, ">%s", options); - options = lua_tostring(L, -1); - lua_pushvalue(L, 1); - } - else - return luaL_argerror(L, 1, "function or level expected"); - if (!lua_getinfo(L, options, &ar)) - return luaL_argerror(L, 2, "invalid option"); - lua_newtable(L); - for (; *options; options++) { - switch (*options) { - case 'S': - settabss(L, "source", ar.source); - settabss(L, "short_src", ar.short_src); - settabsi(L, "linedefined", ar.linedefined); - settabss(L, "what", ar.what); - break; - case 'l': - settabsi(L, "currentline", ar.currentline); - break; - case 'u': - settabsi(L, "nups", ar.nups); - break; - case 'n': - settabss(L, "name", ar.name); - settabss(L, "namewhat", ar.namewhat); - break; - case 'f': - lua_pushliteral(L, "func"); - lua_pushvalue(L, -3); - lua_rawset(L, -3); - break; - } - } - return 1; /* return table */ -} - - -static int getlocal (lua_State *L) { - lua_Debug ar; - const char *name; - if (!lua_getstack(L, luaL_checkint(L, 1), &ar)) /* level out of range? */ - return luaL_argerror(L, 1, "level out of range"); - name = lua_getlocal(L, &ar, luaL_checkint(L, 2)); - if (name) { - lua_pushstring(L, name); - lua_pushvalue(L, -2); - return 2; - } - else { - lua_pushnil(L); - return 1; - } -} - - -static int setlocal (lua_State *L) { - lua_Debug ar; - if (!lua_getstack(L, luaL_checkint(L, 1), &ar)) /* level out of range? */ - return luaL_argerror(L, 1, "level out of range"); - luaL_checkany(L, 3); - lua_pushstring(L, lua_setlocal(L, &ar, luaL_checkint(L, 2))); - return 1; -} - - -static int auxupvalue (lua_State *L, int get) { - const char *name; - int n = luaL_checkint(L, 2); - luaL_checktype(L, 1, LUA_TFUNCTION); - if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */ - name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); - if (name == NULL) return 0; - lua_pushstring(L, name); - lua_insert(L, -(get+1)); - return get + 1; -} - - -static int getupvalue (lua_State *L) { - return auxupvalue(L, 1); -} - - -static int setupvalue (lua_State *L) { - luaL_checkany(L, 3); - return auxupvalue(L, 0); -} - - - -static const char KEY_HOOK = 'h'; - - -static void hookf (lua_State *L, lua_Debug *ar) { - static const char *const hooknames[] = - {"call", "return", "line", "count", "tail return"}; - lua_pushlightuserdata(L, (void *)&KEY_HOOK); - lua_rawget(L, LUA_REGISTRYINDEX); - if (lua_isfunction(L, -1)) { - lua_pushstring(L, hooknames[(int)ar->event]); - if (ar->currentline >= 0) - lua_pushnumber(L, (lua_Number)ar->currentline); - else lua_pushnil(L); - lua_assert(lua_getinfo(L, "lS", ar)); - lua_call(L, 2, 0); - } - else - lua_pop(L, 1); /* pop result from gettable */ -} - - -static int makemask (const char *smask, int count) { - int mask = 0; - if (strchr(smask, 'c')) mask |= LUA_MASKCALL; - if (strchr(smask, 'r')) mask |= LUA_MASKRET; - if (strchr(smask, 'l')) mask |= LUA_MASKLINE; - if (count > 0) mask |= LUA_MASKCOUNT; - return mask; -} - - -static char *unmakemask (int mask, char *smask) { - int i = 0; - if (mask & LUA_MASKCALL) smask[i++] = 'c'; - if (mask & LUA_MASKRET) smask[i++] = 'r'; - if (mask & LUA_MASKLINE) smask[i++] = 'l'; - smask[i] = '\0'; - return smask; -} - - -static int sethook (lua_State *L) { - if (lua_isnoneornil(L, 1)) { - lua_settop(L, 1); - lua_sethook(L, NULL, 0, 0); /* turn off hooks */ - } - else { - const char *smask = luaL_checkstring(L, 2); - int count = luaL_optint(L, 3, 0); - luaL_checktype(L, 1, LUA_TFUNCTION); - lua_sethook(L, hookf, makemask(smask, count), count); - } - lua_pushlightuserdata(L, (void *)&KEY_HOOK); - lua_pushvalue(L, 1); - lua_rawset(L, LUA_REGISTRYINDEX); /* set new hook */ - return 0; -} - - -static int gethook (lua_State *L) { - char buff[5]; - int mask = lua_gethookmask(L); - lua_Hook hook = lua_gethook(L); - if (hook != NULL && hook != hookf) /* external hook? */ - lua_pushliteral(L, "external hook"); - else { - lua_pushlightuserdata(L, (void *)&KEY_HOOK); - lua_rawget(L, LUA_REGISTRYINDEX); /* get hook */ - } - lua_pushstring(L, unmakemask(mask, buff)); - lua_pushnumber(L, (lua_Number)lua_gethookcount(L)); - return 3; -} - - -static int debug (lua_State *L) { - for (;;) { - char buffer[250]; - fputs("lua_debug> ", stderr); - if (fgets(buffer, sizeof(buffer), stdin) == 0 || - strcmp(buffer, "cont\n") == 0) - return 0; - lua_dostring(L, buffer); - lua_settop(L, 0); /* remove eventual returns */ - } -} - - -#define LEVELS1 12 /* size of the first part of the stack */ -#define LEVELS2 10 /* size of the second part of the stack */ - -static int errorfb (lua_State *L) { - int level = 1; /* skip level 0 (it's this function) */ - int firstpart = 1; /* still before eventual `...' */ - lua_Debug ar; - if (lua_gettop(L) == 0) - lua_pushliteral(L, ""); - else if (!lua_isstring(L, 1)) return 1; /* no string message */ - else lua_pushliteral(L, "\n"); - lua_pushliteral(L, "stack traceback:"); - while (lua_getstack(L, level++, &ar)) { - if (level > LEVELS1 && firstpart) { - /* no more than `LEVELS2' more levels? */ - if (!lua_getstack(L, level+LEVELS2, &ar)) - level--; /* keep going */ - else { - lua_pushliteral(L, "\n\t..."); /* too many levels */ - while (lua_getstack(L, level+LEVELS2, &ar)) /* find last levels */ - level++; - } - firstpart = 0; - continue; - } - lua_pushliteral(L, "\n\t"); - lua_getinfo(L, "Snl", &ar); - lua_pushfstring(L, "%s:", ar.short_src); - if (ar.currentline > 0) - lua_pushfstring(L, "%d:", ar.currentline); - switch (*ar.namewhat) { - case 'g': /* global */ - case 'l': /* local */ - case 'f': /* field */ - case 'm': /* method */ - lua_pushfstring(L, " in function `%s'", ar.name); - break; - default: { - if (*ar.what == 'm') /* main? */ - lua_pushfstring(L, " in main chunk"); - else if (*ar.what == 'C' || *ar.what == 't') - lua_pushliteral(L, " ?"); /* C function or tail call */ - else - lua_pushfstring(L, " in function <%s:%d>", - ar.short_src, ar.linedefined); - } - } - lua_concat(L, lua_gettop(L)); - } - lua_concat(L, lua_gettop(L)); - return 1; -} - - -static const luaL_reg dblib[] = { - {"getlocal", getlocal}, - {"getinfo", getinfo}, - {"gethook", gethook}, - {"getupvalue", getupvalue}, - {"sethook", sethook}, - {"setlocal", setlocal}, - {"setupvalue", setupvalue}, - {"debug", debug}, - {"traceback", errorfb}, - {NULL, NULL} -}; - - -LUALIB_API int luaopen_debug (lua_State *L) { - luaL_openlib(L, LUA_DBLIBNAME, dblib, 0); - lua_pushliteral(L, "_TRACEBACK"); - lua_pushcfunction(L, errorfb); - lua_settable(L, LUA_GLOBALSINDEX); - return 1; -} - +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include +#include +#include + +#define ldblib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + +static void settabss (lua_State *L, const char *i, const char *v) { + lua_pushstring(L, i); + lua_pushstring(L, v); + lua_rawset(L, -3); +} + + +static void settabsi (lua_State *L, const char *i, int v) { + lua_pushstring(L, i); + lua_pushnumber(L, (lua_Number)v); + lua_rawset(L, -3); +} + + +static int getinfo (lua_State *L) { + lua_Debug ar; + const char *options = luaL_optstring(L, 2, "flnSu"); + if (lua_isnumber(L, 1)) { + if (!lua_getstack(L, (int)(lua_tonumber(L, 1)), &ar)) { + lua_pushnil(L); /* level out of range */ + return 1; + } + } + else if (lua_isfunction(L, 1)) { + lua_pushfstring(L, ">%s", options); + options = lua_tostring(L, -1); + lua_pushvalue(L, 1); + } + else + return luaL_argerror(L, 1, "function or level expected"); + if (!lua_getinfo(L, options, &ar)) + return luaL_argerror(L, 2, "invalid option"); + lua_newtable(L); + for (; *options; options++) { + switch (*options) { + case 'S': + settabss(L, "source", ar.source); + settabss(L, "short_src", ar.short_src); + settabsi(L, "linedefined", ar.linedefined); + settabss(L, "what", ar.what); + break; + case 'l': + settabsi(L, "currentline", ar.currentline); + break; + case 'u': + settabsi(L, "nups", ar.nups); + break; + case 'n': + settabss(L, "name", ar.name); + settabss(L, "namewhat", ar.namewhat); + break; + case 'f': + lua_pushliteral(L, "func"); + lua_pushvalue(L, -3); + lua_rawset(L, -3); + break; + } + } + return 1; /* return table */ +} + + +static int getlocal (lua_State *L) { + lua_Debug ar; + const char *name; + if (!lua_getstack(L, luaL_checkint(L, 1), &ar)) /* level out of range? */ + return luaL_argerror(L, 1, "level out of range"); + name = lua_getlocal(L, &ar, luaL_checkint(L, 2)); + if (name) { + lua_pushstring(L, name); + lua_pushvalue(L, -2); + return 2; + } + else { + lua_pushnil(L); + return 1; + } +} + + +static int setlocal (lua_State *L) { + lua_Debug ar; + if (!lua_getstack(L, luaL_checkint(L, 1), &ar)) /* level out of range? */ + return luaL_argerror(L, 1, "level out of range"); + luaL_checkany(L, 3); + lua_pushstring(L, lua_setlocal(L, &ar, luaL_checkint(L, 2))); + return 1; +} + + +static int auxupvalue (lua_State *L, int get) { + const char *name; + int n = luaL_checkint(L, 2); + luaL_checktype(L, 1, LUA_TFUNCTION); + if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */ + name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); + if (name == NULL) return 0; + lua_pushstring(L, name); + lua_insert(L, -(get+1)); + return get + 1; +} + + +static int getupvalue (lua_State *L) { + return auxupvalue(L, 1); +} + + +static int setupvalue (lua_State *L) { + luaL_checkany(L, 3); + return auxupvalue(L, 0); +} + + + +static const char KEY_HOOK = 'h'; + + +static void hookf (lua_State *L, lua_Debug *ar) { + static const char *const hooknames[] = + {"call", "return", "line", "count", "tail return"}; + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_rawget(L, LUA_REGISTRYINDEX); + if (lua_isfunction(L, -1)) { + lua_pushstring(L, hooknames[(int)ar->event]); + if (ar->currentline >= 0) + lua_pushnumber(L, (lua_Number)ar->currentline); + else lua_pushnil(L); + lua_assert(lua_getinfo(L, "lS", ar)); + lua_call(L, 2, 0); + } + else + lua_pop(L, 1); /* pop result from gettable */ +} + + +static int makemask (const char *smask, int count) { + int mask = 0; + if (strchr(smask, 'c')) mask |= LUA_MASKCALL; + if (strchr(smask, 'r')) mask |= LUA_MASKRET; + if (strchr(smask, 'l')) mask |= LUA_MASKLINE; + if (count > 0) mask |= LUA_MASKCOUNT; + return mask; +} + + +static char *unmakemask (int mask, char *smask) { + int i = 0; + if (mask & LUA_MASKCALL) smask[i++] = 'c'; + if (mask & LUA_MASKRET) smask[i++] = 'r'; + if (mask & LUA_MASKLINE) smask[i++] = 'l'; + smask[i] = '\0'; + return smask; +} + + +static int sethook (lua_State *L) { + if (lua_isnoneornil(L, 1)) { + lua_settop(L, 1); + lua_sethook(L, NULL, 0, 0); /* turn off hooks */ + } + else { + const char *smask = luaL_checkstring(L, 2); + int count = luaL_optint(L, 3, 0); + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_sethook(L, hookf, makemask(smask, count), count); + } + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_pushvalue(L, 1); + lua_rawset(L, LUA_REGISTRYINDEX); /* set new hook */ + return 0; +} + + +static int gethook (lua_State *L) { + char buff[5]; + int mask = lua_gethookmask(L); + lua_Hook hook = lua_gethook(L); + if (hook != NULL && hook != hookf) /* external hook? */ + lua_pushliteral(L, "external hook"); + else { + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_rawget(L, LUA_REGISTRYINDEX); /* get hook */ + } + lua_pushstring(L, unmakemask(mask, buff)); + lua_pushnumber(L, (lua_Number)lua_gethookcount(L)); + return 3; +} + + +static int debug (lua_State *L) { + for (;;) { + char buffer[250]; + fputs("lua_debug> ", stderr); + if (fgets(buffer, sizeof(buffer), stdin) == 0 || + strcmp(buffer, "cont\n") == 0) + return 0; + lua_dostring(L, buffer); + lua_settop(L, 0); /* remove eventual returns */ + } +} + + +#define LEVELS1 12 /* size of the first part of the stack */ +#define LEVELS2 10 /* size of the second part of the stack */ + +static int errorfb (lua_State *L) { + int level = 1; /* skip level 0 (it's this function) */ + int firstpart = 1; /* still before eventual `...' */ + lua_Debug ar; + if (lua_gettop(L) == 0) + lua_pushliteral(L, ""); + else if (!lua_isstring(L, 1)) return 1; /* no string message */ + else lua_pushliteral(L, "\n"); + lua_pushliteral(L, "stack traceback:"); + while (lua_getstack(L, level++, &ar)) { + if (level > LEVELS1 && firstpart) { + /* no more than `LEVELS2' more levels? */ + if (!lua_getstack(L, level+LEVELS2, &ar)) + level--; /* keep going */ + else { + lua_pushliteral(L, "\n\t..."); /* too many levels */ + while (lua_getstack(L, level+LEVELS2, &ar)) /* find last levels */ + level++; + } + firstpart = 0; + continue; + } + lua_pushliteral(L, "\n\t"); + lua_getinfo(L, "Snl", &ar); + lua_pushfstring(L, "%s:", ar.short_src); + if (ar.currentline > 0) + lua_pushfstring(L, "%d:", ar.currentline); + switch (*ar.namewhat) { + case 'g': /* global */ + case 'l': /* local */ + case 'f': /* field */ + case 'm': /* method */ + lua_pushfstring(L, " in function `%s'", ar.name); + break; + default: { + if (*ar.what == 'm') /* main? */ + lua_pushfstring(L, " in main chunk"); + else if (*ar.what == 'C' || *ar.what == 't') + lua_pushliteral(L, " ?"); /* C function or tail call */ + else + lua_pushfstring(L, " in function <%s:%d>", + ar.short_src, ar.linedefined); + } + } + lua_concat(L, lua_gettop(L)); + } + lua_concat(L, lua_gettop(L)); + return 1; +} + + +static const luaL_reg dblib[] = { + {"getlocal", getlocal}, + {"getinfo", getinfo}, + {"gethook", gethook}, + {"getupvalue", getupvalue}, + {"sethook", sethook}, + {"setlocal", setlocal}, + {"setupvalue", setupvalue}, + {"debug", debug}, + {"traceback", errorfb}, + {NULL, NULL} +}; + + +LUALIB_API int luaopen_debug (lua_State *L) { + luaL_openlib(L, LUA_DBLIBNAME, dblib, 0); + lua_pushliteral(L, "_TRACEBACK"); + lua_pushcfunction(L, errorfb); + lua_settable(L, LUA_GLOBALSINDEX); + return 1; +} + diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/liolib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/liolib.c index 01281e2733..1aa0cc5bc3 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/liolib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/liolib.c @@ -1,760 +1,760 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include -#include -#include -#include -#include -#include - -#define liolib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - - -/* -** by default, gcc does not get `tmpname' -*/ -#ifndef USE_TMPNAME -#ifdef __GNUC__ -#define USE_TMPNAME 0 -#else -#define USE_TMPNAME 1 -#endif -#endif - - -/* -** by default, posix systems get `popen' -*/ -#ifndef USE_POPEN -#ifdef _POSIX_C_SOURCE -#if _POSIX_C_SOURCE >= 2 -#define USE_POPEN 1 -#endif -#endif -#endif - -#ifndef USE_POPEN -#define USE_POPEN 0 -#endif - - - - -/* -** {====================================================== -** FILE Operations -** ======================================================= -*/ - - -#if !USE_POPEN -#define pclose(f) (-1) -#endif - - -#define FILEHANDLE "FILE*" - -#define IO_INPUT "_input" -#define IO_OUTPUT "_output" - - -static int pushresult (lua_State *L, int i, const char *filename) { - if (i) { - lua_pushboolean(L, 1); - return 1; - } - else { - lua_pushnil(L); - if (filename) - lua_pushfstring(L, "%s: %s", filename, strerror(errno)); - else - lua_pushfstring(L, "%s", strerror(errno)); - lua_pushnumber(L, errno); - return 3; - } -} - - -static FILE **topfile (lua_State *L, int findex) { - FILE **f = (FILE **)luaL_checkudata(L, findex, FILEHANDLE); - if (f == NULL) luaL_argerror(L, findex, "bad file"); - return f; -} - - -static int io_type (lua_State *L) { - FILE **f = (FILE **)luaL_checkudata(L, 1, FILEHANDLE); - if (f == NULL) lua_pushnil(L); - else if (*f == NULL) - lua_pushliteral(L, "closed file"); - else - lua_pushliteral(L, "file"); - return 1; -} - - -static FILE *tofile (lua_State *L, int findex) { - FILE **f = topfile(L, findex); - if (*f == NULL) - luaL_error(L, "attempt to use a closed file"); - return *f; -} - - - -/* -** When creating file handles, always creates a `closed' file handle -** before opening the actual file; so, if there is a memory error, the -** file is not left opened. -*/ -static FILE **newfile (lua_State *L) { - FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *)); - *pf = NULL; /* file handle is currently `closed' */ - luaL_getmetatable(L, FILEHANDLE); - lua_setmetatable(L, -2); - return pf; -} - - -/* -** assumes that top of the stack is the `io' library, and next is -** the `io' metatable -*/ -static void registerfile (lua_State *L, FILE *f, const char *name, - const char *impname) { - lua_pushstring(L, name); - *newfile(L) = f; - if (impname) { - lua_pushstring(L, impname); - lua_pushvalue(L, -2); - lua_settable(L, -6); /* metatable[impname] = file */ - } - lua_settable(L, -3); /* io[name] = file */ -} - - -static int aux_close (lua_State *L) { - FILE *f = tofile(L, 1); - if (f == stdin || f == stdout || f == stderr) - return 0; /* file cannot be closed */ - else { - int ok = (pclose(f) != -1) || (fclose(f) == 0); - if (ok) - *(FILE **)lua_touserdata(L, 1) = NULL; /* mark file as closed */ - return ok; - } -} - - -static int io_close (lua_State *L) { - if (lua_isnone(L, 1)) { - lua_pushstring(L, IO_OUTPUT); - lua_rawget(L, lua_upvalueindex(1)); - } - return pushresult(L, aux_close(L), NULL); -} - - -static int io_gc (lua_State *L) { - FILE **f = topfile(L, 1); - if (*f != NULL) /* ignore closed files */ - aux_close(L); - return 0; -} - - -static int io_tostring (lua_State *L) { - char buff[32]; - FILE **f = topfile(L, 1); - if (*f == NULL) - strcpy(buff, "closed"); - else - sprintf(buff, "%p", lua_touserdata(L, 1)); - lua_pushfstring(L, "file (%s)", buff); - return 1; -} - - -static int io_open (lua_State *L) { - const char *filename = luaL_checkstring(L, 1); - const char *mode = luaL_optstring(L, 2, "r"); - FILE **pf = newfile(L); - *pf = fopen(filename, mode); - return (*pf == NULL) ? pushresult(L, 0, filename) : 1; -} - - -static int io_popen (lua_State *L) { -#if !USE_POPEN - luaL_error(L, "`popen' not supported"); - return 0; -#else - const char *filename = luaL_checkstring(L, 1); - const char *mode = luaL_optstring(L, 2, "r"); - FILE **pf = newfile(L); - *pf = popen(filename, mode); - return (*pf == NULL) ? pushresult(L, 0, filename) : 1; -#endif -} - - -static int io_tmpfile (lua_State *L) { - FILE **pf = newfile(L); - *pf = tmpfile(); - return (*pf == NULL) ? pushresult(L, 0, NULL) : 1; -} - - -static FILE *getiofile (lua_State *L, const char *name) { - lua_pushstring(L, name); - lua_rawget(L, lua_upvalueindex(1)); - return tofile(L, -1); -} - - -static int g_iofile (lua_State *L, const char *name, const char *mode) { - if (!lua_isnoneornil(L, 1)) { - const char *filename = lua_tostring(L, 1); - lua_pushstring(L, name); - if (filename) { - FILE **pf = newfile(L); - *pf = fopen(filename, mode); - if (*pf == NULL) { - lua_pushfstring(L, "%s: %s", filename, strerror(errno)); - luaL_argerror(L, 1, lua_tostring(L, -1)); - } - } - else { - tofile(L, 1); /* check that it's a valid file handle */ - lua_pushvalue(L, 1); - } - lua_rawset(L, lua_upvalueindex(1)); - } - /* return current value */ - lua_pushstring(L, name); - lua_rawget(L, lua_upvalueindex(1)); - return 1; -} - - -static int io_input (lua_State *L) { - return g_iofile(L, IO_INPUT, "r"); -} - - -static int io_output (lua_State *L) { - return g_iofile(L, IO_OUTPUT, "w"); -} - - -static int io_readline (lua_State *L); - - -static void aux_lines (lua_State *L, int idx, int close) { - lua_pushliteral(L, FILEHANDLE); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_pushvalue(L, idx); - lua_pushboolean(L, close); /* close/not close file when finished */ - lua_pushcclosure(L, io_readline, 3); -} - - -static int f_lines (lua_State *L) { - tofile(L, 1); /* check that it's a valid file handle */ - aux_lines(L, 1, 0); - return 1; -} - - -static int io_lines (lua_State *L) { - if (lua_isnoneornil(L, 1)) { /* no arguments? */ - lua_pushstring(L, IO_INPUT); - lua_rawget(L, lua_upvalueindex(1)); /* will iterate over default input */ - return f_lines(L); - } - else { - const char *filename = luaL_checkstring(L, 1); - FILE **pf = newfile(L); - *pf = fopen(filename, "r"); - luaL_argcheck(L, *pf, 1, strerror(errno)); - aux_lines(L, lua_gettop(L), 1); - return 1; - } -} - - -/* -** {====================================================== -** READ -** ======================================================= -*/ - - -static int read_number (lua_State *L, FILE *f) { - lua_Number d; - if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { - lua_pushnumber(L, d); - return 1; - } - else return 0; /* read fails */ -} - - -static int test_eof (lua_State *L, FILE *f) { - int c = getc(f); - ungetc(c, f); - lua_pushlstring(L, NULL, 0); - return (c != EOF); -} - - -static int read_line (lua_State *L, FILE *f) { - luaL_Buffer b; - luaL_buffinit(L, &b); - for (;;) { - size_t l; - char *p = luaL_prepbuffer(&b); - if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ - luaL_pushresult(&b); /* close buffer */ - return (lua_strlen(L, -1) > 0); /* check whether read something */ - } - l = strlen(p); - if (p[l-1] != '\n') - luaL_addsize(&b, l); - else { - luaL_addsize(&b, l - 1); /* do not include `eol' */ - luaL_pushresult(&b); /* close buffer */ - return 1; /* read at least an `eol' */ - } - } -} - - -static int read_chars (lua_State *L, FILE *f, size_t n) { - size_t rlen; /* how much to read */ - size_t nr; /* number of chars actually read */ - luaL_Buffer b; - luaL_buffinit(L, &b); - rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ - do { - char *p = luaL_prepbuffer(&b); - if (rlen > n) rlen = n; /* cannot read more than asked */ - nr = fread(p, sizeof(char), rlen, f); - luaL_addsize(&b, nr); - n -= nr; /* still have to read `n' chars */ - } while (n > 0 && nr == rlen); /* until end of count or eof */ - luaL_pushresult(&b); /* close buffer */ - return (n == 0 || lua_strlen(L, -1) > 0); -} - - -static int g_read (lua_State *L, FILE *f, int first) { - int nargs = lua_gettop(L) - 1; - int success; - int n; - if (nargs == 0) { /* no arguments? */ - success = read_line(L, f); - n = first+1; /* to return 1 result */ - } - else { /* ensure stack space for all results and for auxlib's buffer */ - luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); - success = 1; - for (n = first; nargs-- && success; n++) { - if (lua_type(L, n) == LUA_TNUMBER) { - size_t l = (size_t)lua_tonumber(L, n); - success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); - } - else { - const char *p = lua_tostring(L, n); - luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); - switch (p[1]) { - case 'n': /* number */ - success = read_number(L, f); - break; - case 'l': /* line */ - success = read_line(L, f); - break; - case 'a': /* file */ - read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */ - success = 1; /* always success */ - break; - case 'w': /* word */ - return luaL_error(L, "obsolete option `*w' to `read'"); - default: - return luaL_argerror(L, n, "invalid format"); - } - } - } - } - if (!success) { - lua_pop(L, 1); /* remove last result */ - lua_pushnil(L); /* push nil instead */ - } - return n - first; -} - - -static int io_read (lua_State *L) { - return g_read(L, getiofile(L, IO_INPUT), 1); -} - - -static int f_read (lua_State *L) { - return g_read(L, tofile(L, 1), 2); -} - - -static int io_readline (lua_State *L) { - FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(2)); - if (f == NULL) /* file is already closed? */ - luaL_error(L, "file is already closed"); - if (read_line(L, f)) return 1; - else { /* EOF */ - if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */ - lua_settop(L, 0); - lua_pushvalue(L, lua_upvalueindex(2)); - aux_close(L); /* close it */ - } - return 0; - } -} - -/* }====================================================== */ - - -static int g_write (lua_State *L, FILE *f, int arg) { - int nargs = lua_gettop(L) - 1; - int status = 1; - for (; nargs--; arg++) { - if (lua_type(L, arg) == LUA_TNUMBER) { - /* optimization: could be done exactly as for strings */ - status = status && - fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; - } - else { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); - status = status && (fwrite(s, sizeof(char), l, f) == l); - } - } - return pushresult(L, status, NULL); -} - - -static int io_write (lua_State *L) { - return g_write(L, getiofile(L, IO_OUTPUT), 1); -} - - -static int f_write (lua_State *L) { - return g_write(L, tofile(L, 1), 2); -} - - -static int f_seek (lua_State *L) { - static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; - static const char *const modenames[] = {"set", "cur", "end", NULL}; - FILE *f = tofile(L, 1); - int op = luaL_findstring(luaL_optstring(L, 2, "cur"), modenames); - long offset = luaL_optlong(L, 3, 0); - luaL_argcheck(L, op != -1, 2, "invalid mode"); - op = fseek(f, offset, mode[op]); - if (op) - return pushresult(L, 0, NULL); /* error */ - else { - lua_pushnumber(L, ftell(f)); - return 1; - } -} - - -static int io_flush (lua_State *L) { - return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); -} - - -static int f_flush (lua_State *L) { - return pushresult(L, fflush(tofile(L, 1)) == 0, NULL); -} - - -static const luaL_reg iolib[] = { - {"input", io_input}, - {"output", io_output}, - {"lines", io_lines}, - {"close", io_close}, - {"flush", io_flush}, - {"open", io_open}, - {"popen", io_popen}, - {"read", io_read}, - {"tmpfile", io_tmpfile}, - {"type", io_type}, - {"write", io_write}, - {NULL, NULL} -}; - - -static const luaL_reg flib[] = { - {"flush", f_flush}, - {"read", f_read}, - {"lines", f_lines}, - {"seek", f_seek}, - {"write", f_write}, - {"close", io_close}, - {"__gc", io_gc}, - {"__tostring", io_tostring}, - {NULL, NULL} -}; - - -static void createmeta (lua_State *L) { - luaL_newmetatable(L, FILEHANDLE); /* create new metatable for file handles */ - /* file methods */ - lua_pushliteral(L, "__index"); - lua_pushvalue(L, -2); /* push metatable */ - lua_rawset(L, -3); /* metatable.__index = metatable */ - luaL_openlib(L, NULL, flib, 0); -} - -/* }====================================================== */ - - -/* -** {====================================================== -** Other O.S. Operations -** ======================================================= -*/ - -static int io_execute (lua_State *L) { - lua_pushnumber(L, system(luaL_checkstring(L, 1))); - return 1; -} - - -static int io_remove (lua_State *L) { - const char *filename = luaL_checkstring(L, 1); - return pushresult(L, remove(filename) == 0, filename); -} - - -static int io_rename (lua_State *L) { - const char *fromname = luaL_checkstring(L, 1); - const char *toname = luaL_checkstring(L, 2); - return pushresult(L, rename(fromname, toname) == 0, fromname); -} - - -static int io_tmpname (lua_State *L) { -#if !USE_TMPNAME - luaL_error(L, "`tmpname' not supported"); - return 0; -#else - char buff[L_tmpnam]; - if (tmpnam(buff) != buff) - return luaL_error(L, "unable to generate a unique filename in `tmpname'"); - lua_pushstring(L, buff); - return 1; -#endif -} - - -static int io_getenv (lua_State *L) { - lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ - return 1; -} - - -static int io_clock (lua_State *L) { - lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); - return 1; -} - - -/* -** {====================================================== -** Time/Date operations -** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, -** wday=%w+1, yday=%j, isdst=? } -** ======================================================= -*/ - -static void setfield (lua_State *L, const char *key, int value) { - lua_pushstring(L, key); - lua_pushnumber(L, value); - lua_rawset(L, -3); -} - -static void setboolfield (lua_State *L, const char *key, int value) { - lua_pushstring(L, key); - lua_pushboolean(L, value); - lua_rawset(L, -3); -} - -static int getboolfield (lua_State *L, const char *key) { - int res; - lua_pushstring(L, key); - lua_gettable(L, -2); - res = lua_toboolean(L, -1); - lua_pop(L, 1); - return res; -} - - -static int getfield (lua_State *L, const char *key, int d) { - int res; - lua_pushstring(L, key); - lua_gettable(L, -2); - if (lua_isnumber(L, -1)) - res = (int)(lua_tonumber(L, -1)); - else { - if (d == -2) - return luaL_error(L, "field `%s' missing in date table", key); - res = d; - } - lua_pop(L, 1); - return res; -} - - -static int io_date (lua_State *L) { - const char *s = luaL_optstring(L, 1, "%c"); - time_t t = (time_t)(luaL_optnumber(L, 2, -1)); - struct tm *stm; - if (t == (time_t)(-1)) /* no time given? */ - t = time(NULL); /* use current time */ - if (*s == '!') { /* UTC? */ - stm = gmtime(&t); - s++; /* skip `!' */ - } - else - stm = localtime(&t); - if (stm == NULL) /* invalid date? */ - lua_pushnil(L); - else if (strcmp(s, "*t") == 0) { - lua_newtable(L); - setfield(L, "sec", stm->tm_sec); - setfield(L, "min", stm->tm_min); - setfield(L, "hour", stm->tm_hour); - setfield(L, "day", stm->tm_mday); - setfield(L, "month", stm->tm_mon+1); - setfield(L, "year", stm->tm_year+1900); - setfield(L, "wday", stm->tm_wday+1); - setfield(L, "yday", stm->tm_yday+1); - setboolfield(L, "isdst", stm->tm_isdst); - } - else { - char b[256]; - if (strftime(b, sizeof(b), s, stm)) - lua_pushstring(L, b); - else - return luaL_error(L, "`date' format too long"); - } - return 1; -} - - -static int io_time (lua_State *L) { - if (lua_isnoneornil(L, 1)) /* called without args? */ - lua_pushnumber(L, time(NULL)); /* return current time */ - else { - time_t t; - struct tm ts; - luaL_checktype(L, 1, LUA_TTABLE); - lua_settop(L, 1); /* make sure table is at the top */ - ts.tm_sec = getfield(L, "sec", 0); - ts.tm_min = getfield(L, "min", 0); - ts.tm_hour = getfield(L, "hour", 12); - ts.tm_mday = getfield(L, "day", -2); - ts.tm_mon = getfield(L, "month", -2) - 1; - ts.tm_year = getfield(L, "year", -2) - 1900; - ts.tm_isdst = getboolfield(L, "isdst"); - t = mktime(&ts); - if (t == (time_t)(-1)) - lua_pushnil(L); - else - lua_pushnumber(L, t); - } - return 1; -} - - -static int io_difftime (lua_State *L) { - lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), - (time_t)(luaL_optnumber(L, 2, 0)))); - return 1; -} - -/* }====================================================== */ - - -static int io_setloc (lua_State *L) { - static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, - LC_NUMERIC, LC_TIME}; - static const char *const catnames[] = {"all", "collate", "ctype", "monetary", - "numeric", "time", NULL}; - const char *l = lua_tostring(L, 1); - int op = luaL_findstring(luaL_optstring(L, 2, "all"), catnames); - luaL_argcheck(L, l || lua_isnoneornil(L, 1), 1, "string expected"); - luaL_argcheck(L, op != -1, 2, "invalid option"); - lua_pushstring(L, setlocale(cat[op], l)); - return 1; -} - - -static int io_exit (lua_State *L) { - exit(luaL_optint(L, 1, EXIT_SUCCESS)); - return 0; /* to avoid warnings */ -} - -static const luaL_reg syslib[] = { - {"clock", io_clock}, - {"date", io_date}, - {"difftime", io_difftime}, - {"execute", io_execute}, - {"exit", io_exit}, - {"getenv", io_getenv}, - {"remove", io_remove}, - {"rename", io_rename}, - {"setlocale", io_setloc}, - {"time", io_time}, - {"tmpname", io_tmpname}, - {NULL, NULL} -}; - -/* }====================================================== */ - - - -LUALIB_API int luaopen_io (lua_State *L) { - luaL_openlib(L, LUA_OSLIBNAME, syslib, 0); - createmeta(L); - lua_pushvalue(L, -1); - luaL_openlib(L, LUA_IOLIBNAME, iolib, 1); - /* put predefined file handles into `io' table */ - registerfile(L, stdin, "stdin", IO_INPUT); - registerfile(L, stdout, "stdout", IO_OUTPUT); - registerfile(L, stderr, "stderr", NULL); - return 1; -} - +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include +#include +#include +#include +#include +#include + +#define liolib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + +/* +** by default, gcc does not get `tmpname' +*/ +#ifndef USE_TMPNAME +#ifdef __GNUC__ +#define USE_TMPNAME 0 +#else +#define USE_TMPNAME 1 +#endif +#endif + + +/* +** by default, posix systems get `popen' +*/ +#ifndef USE_POPEN +#ifdef _POSIX_C_SOURCE +#if _POSIX_C_SOURCE >= 2 +#define USE_POPEN 1 +#endif +#endif +#endif + +#ifndef USE_POPEN +#define USE_POPEN 0 +#endif + + + + +/* +** {====================================================== +** FILE Operations +** ======================================================= +*/ + + +#if !USE_POPEN +#define pclose(f) (-1) +#endif + + +#define FILEHANDLE "FILE*" + +#define IO_INPUT "_input" +#define IO_OUTPUT "_output" + + +static int pushresult (lua_State *L, int i, const char *filename) { + if (i) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushnil(L); + if (filename) + lua_pushfstring(L, "%s: %s", filename, strerror(errno)); + else + lua_pushfstring(L, "%s", strerror(errno)); + lua_pushnumber(L, errno); + return 3; + } +} + + +static FILE **topfile (lua_State *L, int findex) { + FILE **f = (FILE **)luaL_checkudata(L, findex, FILEHANDLE); + if (f == NULL) luaL_argerror(L, findex, "bad file"); + return f; +} + + +static int io_type (lua_State *L) { + FILE **f = (FILE **)luaL_checkudata(L, 1, FILEHANDLE); + if (f == NULL) lua_pushnil(L); + else if (*f == NULL) + lua_pushliteral(L, "closed file"); + else + lua_pushliteral(L, "file"); + return 1; +} + + +static FILE *tofile (lua_State *L, int findex) { + FILE **f = topfile(L, findex); + if (*f == NULL) + luaL_error(L, "attempt to use a closed file"); + return *f; +} + + + +/* +** When creating file handles, always creates a `closed' file handle +** before opening the actual file; so, if there is a memory error, the +** file is not left opened. +*/ +static FILE **newfile (lua_State *L) { + FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *)); + *pf = NULL; /* file handle is currently `closed' */ + luaL_getmetatable(L, FILEHANDLE); + lua_setmetatable(L, -2); + return pf; +} + + +/* +** assumes that top of the stack is the `io' library, and next is +** the `io' metatable +*/ +static void registerfile (lua_State *L, FILE *f, const char *name, + const char *impname) { + lua_pushstring(L, name); + *newfile(L) = f; + if (impname) { + lua_pushstring(L, impname); + lua_pushvalue(L, -2); + lua_settable(L, -6); /* metatable[impname] = file */ + } + lua_settable(L, -3); /* io[name] = file */ +} + + +static int aux_close (lua_State *L) { + FILE *f = tofile(L, 1); + if (f == stdin || f == stdout || f == stderr) + return 0; /* file cannot be closed */ + else { + int ok = (pclose(f) != -1) || (fclose(f) == 0); + if (ok) + *(FILE **)lua_touserdata(L, 1) = NULL; /* mark file as closed */ + return ok; + } +} + + +static int io_close (lua_State *L) { + if (lua_isnone(L, 1)) { + lua_pushstring(L, IO_OUTPUT); + lua_rawget(L, lua_upvalueindex(1)); + } + return pushresult(L, aux_close(L), NULL); +} + + +static int io_gc (lua_State *L) { + FILE **f = topfile(L, 1); + if (*f != NULL) /* ignore closed files */ + aux_close(L); + return 0; +} + + +static int io_tostring (lua_State *L) { + char buff[32]; + FILE **f = topfile(L, 1); + if (*f == NULL) + strcpy(buff, "closed"); + else + sprintf(buff, "%p", lua_touserdata(L, 1)); + lua_pushfstring(L, "file (%s)", buff); + return 1; +} + + +static int io_open (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + FILE **pf = newfile(L); + *pf = fopen(filename, mode); + return (*pf == NULL) ? pushresult(L, 0, filename) : 1; +} + + +static int io_popen (lua_State *L) { +#if !USE_POPEN + luaL_error(L, "`popen' not supported"); + return 0; +#else + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + FILE **pf = newfile(L); + *pf = popen(filename, mode); + return (*pf == NULL) ? pushresult(L, 0, filename) : 1; +#endif +} + + +static int io_tmpfile (lua_State *L) { + FILE **pf = newfile(L); + *pf = tmpfile(); + return (*pf == NULL) ? pushresult(L, 0, NULL) : 1; +} + + +static FILE *getiofile (lua_State *L, const char *name) { + lua_pushstring(L, name); + lua_rawget(L, lua_upvalueindex(1)); + return tofile(L, -1); +} + + +static int g_iofile (lua_State *L, const char *name, const char *mode) { + if (!lua_isnoneornil(L, 1)) { + const char *filename = lua_tostring(L, 1); + lua_pushstring(L, name); + if (filename) { + FILE **pf = newfile(L); + *pf = fopen(filename, mode); + if (*pf == NULL) { + lua_pushfstring(L, "%s: %s", filename, strerror(errno)); + luaL_argerror(L, 1, lua_tostring(L, -1)); + } + } + else { + tofile(L, 1); /* check that it's a valid file handle */ + lua_pushvalue(L, 1); + } + lua_rawset(L, lua_upvalueindex(1)); + } + /* return current value */ + lua_pushstring(L, name); + lua_rawget(L, lua_upvalueindex(1)); + return 1; +} + + +static int io_input (lua_State *L) { + return g_iofile(L, IO_INPUT, "r"); +} + + +static int io_output (lua_State *L) { + return g_iofile(L, IO_OUTPUT, "w"); +} + + +static int io_readline (lua_State *L); + + +static void aux_lines (lua_State *L, int idx, int close) { + lua_pushliteral(L, FILEHANDLE); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushvalue(L, idx); + lua_pushboolean(L, close); /* close/not close file when finished */ + lua_pushcclosure(L, io_readline, 3); +} + + +static int f_lines (lua_State *L) { + tofile(L, 1); /* check that it's a valid file handle */ + aux_lines(L, 1, 0); + return 1; +} + + +static int io_lines (lua_State *L) { + if (lua_isnoneornil(L, 1)) { /* no arguments? */ + lua_pushstring(L, IO_INPUT); + lua_rawget(L, lua_upvalueindex(1)); /* will iterate over default input */ + return f_lines(L); + } + else { + const char *filename = luaL_checkstring(L, 1); + FILE **pf = newfile(L); + *pf = fopen(filename, "r"); + luaL_argcheck(L, *pf, 1, strerror(errno)); + aux_lines(L, lua_gettop(L), 1); + return 1; + } +} + + +/* +** {====================================================== +** READ +** ======================================================= +*/ + + +static int read_number (lua_State *L, FILE *f) { + lua_Number d; + if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { + lua_pushnumber(L, d); + return 1; + } + else return 0; /* read fails */ +} + + +static int test_eof (lua_State *L, FILE *f) { + int c = getc(f); + ungetc(c, f); + lua_pushlstring(L, NULL, 0); + return (c != EOF); +} + + +static int read_line (lua_State *L, FILE *f) { + luaL_Buffer b; + luaL_buffinit(L, &b); + for (;;) { + size_t l; + char *p = luaL_prepbuffer(&b); + if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ + luaL_pushresult(&b); /* close buffer */ + return (lua_strlen(L, -1) > 0); /* check whether read something */ + } + l = strlen(p); + if (p[l-1] != '\n') + luaL_addsize(&b, l); + else { + luaL_addsize(&b, l - 1); /* do not include `eol' */ + luaL_pushresult(&b); /* close buffer */ + return 1; /* read at least an `eol' */ + } + } +} + + +static int read_chars (lua_State *L, FILE *f, size_t n) { + size_t rlen; /* how much to read */ + size_t nr; /* number of chars actually read */ + luaL_Buffer b; + luaL_buffinit(L, &b); + rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ + do { + char *p = luaL_prepbuffer(&b); + if (rlen > n) rlen = n; /* cannot read more than asked */ + nr = fread(p, sizeof(char), rlen, f); + luaL_addsize(&b, nr); + n -= nr; /* still have to read `n' chars */ + } while (n > 0 && nr == rlen); /* until end of count or eof */ + luaL_pushresult(&b); /* close buffer */ + return (n == 0 || lua_strlen(L, -1) > 0); +} + + +static int g_read (lua_State *L, FILE *f, int first) { + int nargs = lua_gettop(L) - 1; + int success; + int n; + if (nargs == 0) { /* no arguments? */ + success = read_line(L, f); + n = first+1; /* to return 1 result */ + } + else { /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + success = 1; + for (n = first; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)lua_tonumber(L, n); + success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); + } + else { + const char *p = lua_tostring(L, n); + luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); + switch (p[1]) { + case 'n': /* number */ + success = read_number(L, f); + break; + case 'l': /* line */ + success = read_line(L, f); + break; + case 'a': /* file */ + read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */ + success = 1; /* always success */ + break; + case 'w': /* word */ + return luaL_error(L, "obsolete option `*w' to `read'"); + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (!success) { + lua_pop(L, 1); /* remove last result */ + lua_pushnil(L); /* push nil instead */ + } + return n - first; +} + + +static int io_read (lua_State *L) { + return g_read(L, getiofile(L, IO_INPUT), 1); +} + + +static int f_read (lua_State *L) { + return g_read(L, tofile(L, 1), 2); +} + + +static int io_readline (lua_State *L) { + FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(2)); + if (f == NULL) /* file is already closed? */ + luaL_error(L, "file is already closed"); + if (read_line(L, f)) return 1; + else { /* EOF */ + if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */ + lua_settop(L, 0); + lua_pushvalue(L, lua_upvalueindex(2)); + aux_close(L); /* close it */ + } + return 0; + } +} + +/* }====================================================== */ + + +static int g_write (lua_State *L, FILE *f, int arg) { + int nargs = lua_gettop(L) - 1; + int status = 1; + for (; nargs--; arg++) { + if (lua_type(L, arg) == LUA_TNUMBER) { + /* optimization: could be done exactly as for strings */ + status = status && + fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; + } + else { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + status = status && (fwrite(s, sizeof(char), l, f) == l); + } + } + return pushresult(L, status, NULL); +} + + +static int io_write (lua_State *L) { + return g_write(L, getiofile(L, IO_OUTPUT), 1); +} + + +static int f_write (lua_State *L) { + return g_write(L, tofile(L, 1), 2); +} + + +static int f_seek (lua_State *L) { + static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; + static const char *const modenames[] = {"set", "cur", "end", NULL}; + FILE *f = tofile(L, 1); + int op = luaL_findstring(luaL_optstring(L, 2, "cur"), modenames); + long offset = luaL_optlong(L, 3, 0); + luaL_argcheck(L, op != -1, 2, "invalid mode"); + op = fseek(f, offset, mode[op]); + if (op) + return pushresult(L, 0, NULL); /* error */ + else { + lua_pushnumber(L, ftell(f)); + return 1; + } +} + + +static int io_flush (lua_State *L) { + return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); +} + + +static int f_flush (lua_State *L) { + return pushresult(L, fflush(tofile(L, 1)) == 0, NULL); +} + + +static const luaL_reg iolib[] = { + {"input", io_input}, + {"output", io_output}, + {"lines", io_lines}, + {"close", io_close}, + {"flush", io_flush}, + {"open", io_open}, + {"popen", io_popen}, + {"read", io_read}, + {"tmpfile", io_tmpfile}, + {"type", io_type}, + {"write", io_write}, + {NULL, NULL} +}; + + +static const luaL_reg flib[] = { + {"flush", f_flush}, + {"read", f_read}, + {"lines", f_lines}, + {"seek", f_seek}, + {"write", f_write}, + {"close", io_close}, + {"__gc", io_gc}, + {"__tostring", io_tostring}, + {NULL, NULL} +}; + + +static void createmeta (lua_State *L) { + luaL_newmetatable(L, FILEHANDLE); /* create new metatable for file handles */ + /* file methods */ + lua_pushliteral(L, "__index"); + lua_pushvalue(L, -2); /* push metatable */ + lua_rawset(L, -3); /* metatable.__index = metatable */ + luaL_openlib(L, NULL, flib, 0); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Other O.S. Operations +** ======================================================= +*/ + +static int io_execute (lua_State *L) { + lua_pushnumber(L, system(luaL_checkstring(L, 1))); + return 1; +} + + +static int io_remove (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + return pushresult(L, remove(filename) == 0, filename); +} + + +static int io_rename (lua_State *L) { + const char *fromname = luaL_checkstring(L, 1); + const char *toname = luaL_checkstring(L, 2); + return pushresult(L, rename(fromname, toname) == 0, fromname); +} + + +static int io_tmpname (lua_State *L) { +#if !USE_TMPNAME + luaL_error(L, "`tmpname' not supported"); + return 0; +#else + char buff[L_tmpnam]; + if (tmpnam(buff) != buff) + return luaL_error(L, "unable to generate a unique filename in `tmpname'"); + lua_pushstring(L, buff); + return 1; +#endif +} + + +static int io_getenv (lua_State *L) { + lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ + return 1; +} + + +static int io_clock (lua_State *L) { + lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); + return 1; +} + + +/* +** {====================================================== +** Time/Date operations +** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, +** wday=%w+1, yday=%j, isdst=? } +** ======================================================= +*/ + +static void setfield (lua_State *L, const char *key, int value) { + lua_pushstring(L, key); + lua_pushnumber(L, value); + lua_rawset(L, -3); +} + +static void setboolfield (lua_State *L, const char *key, int value) { + lua_pushstring(L, key); + lua_pushboolean(L, value); + lua_rawset(L, -3); +} + +static int getboolfield (lua_State *L, const char *key) { + int res; + lua_pushstring(L, key); + lua_gettable(L, -2); + res = lua_toboolean(L, -1); + lua_pop(L, 1); + return res; +} + + +static int getfield (lua_State *L, const char *key, int d) { + int res; + lua_pushstring(L, key); + lua_gettable(L, -2); + if (lua_isnumber(L, -1)) + res = (int)(lua_tonumber(L, -1)); + else { + if (d == -2) + return luaL_error(L, "field `%s' missing in date table", key); + res = d; + } + lua_pop(L, 1); + return res; +} + + +static int io_date (lua_State *L) { + const char *s = luaL_optstring(L, 1, "%c"); + time_t t = (time_t)(luaL_optnumber(L, 2, -1)); + struct tm *stm; + if (t == (time_t)(-1)) /* no time given? */ + t = time(NULL); /* use current time */ + if (*s == '!') { /* UTC? */ + stm = gmtime(&t); + s++; /* skip `!' */ + } + else + stm = localtime(&t); + if (stm == NULL) /* invalid date? */ + lua_pushnil(L); + else if (strcmp(s, "*t") == 0) { + lua_newtable(L); + setfield(L, "sec", stm->tm_sec); + setfield(L, "min", stm->tm_min); + setfield(L, "hour", stm->tm_hour); + setfield(L, "day", stm->tm_mday); + setfield(L, "month", stm->tm_mon+1); + setfield(L, "year", stm->tm_year+1900); + setfield(L, "wday", stm->tm_wday+1); + setfield(L, "yday", stm->tm_yday+1); + setboolfield(L, "isdst", stm->tm_isdst); + } + else { + char b[256]; + if (strftime(b, sizeof(b), s, stm)) + lua_pushstring(L, b); + else + return luaL_error(L, "`date' format too long"); + } + return 1; +} + + +static int io_time (lua_State *L) { + if (lua_isnoneornil(L, 1)) /* called without args? */ + lua_pushnumber(L, time(NULL)); /* return current time */ + else { + time_t t; + struct tm ts; + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 1); /* make sure table is at the top */ + ts.tm_sec = getfield(L, "sec", 0); + ts.tm_min = getfield(L, "min", 0); + ts.tm_hour = getfield(L, "hour", 12); + ts.tm_mday = getfield(L, "day", -2); + ts.tm_mon = getfield(L, "month", -2) - 1; + ts.tm_year = getfield(L, "year", -2) - 1900; + ts.tm_isdst = getboolfield(L, "isdst"); + t = mktime(&ts); + if (t == (time_t)(-1)) + lua_pushnil(L); + else + lua_pushnumber(L, t); + } + return 1; +} + + +static int io_difftime (lua_State *L) { + lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), + (time_t)(luaL_optnumber(L, 2, 0)))); + return 1; +} + +/* }====================================================== */ + + +static int io_setloc (lua_State *L) { + static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, + LC_NUMERIC, LC_TIME}; + static const char *const catnames[] = {"all", "collate", "ctype", "monetary", + "numeric", "time", NULL}; + const char *l = lua_tostring(L, 1); + int op = luaL_findstring(luaL_optstring(L, 2, "all"), catnames); + luaL_argcheck(L, l || lua_isnoneornil(L, 1), 1, "string expected"); + luaL_argcheck(L, op != -1, 2, "invalid option"); + lua_pushstring(L, setlocale(cat[op], l)); + return 1; +} + + +static int io_exit (lua_State *L) { + exit(luaL_optint(L, 1, EXIT_SUCCESS)); + return 0; /* to avoid warnings */ +} + +static const luaL_reg syslib[] = { + {"clock", io_clock}, + {"date", io_date}, + {"difftime", io_difftime}, + {"execute", io_execute}, + {"exit", io_exit}, + {"getenv", io_getenv}, + {"remove", io_remove}, + {"rename", io_rename}, + {"setlocale", io_setloc}, + {"time", io_time}, + {"tmpname", io_tmpname}, + {NULL, NULL} +}; + +/* }====================================================== */ + + + +LUALIB_API int luaopen_io (lua_State *L) { + luaL_openlib(L, LUA_OSLIBNAME, syslib, 0); + createmeta(L); + lua_pushvalue(L, -1); + luaL_openlib(L, LUA_IOLIBNAME, iolib, 1); + /* put predefined file handles into `io' table */ + registerfile(L, stdin, "stdin", IO_INPUT); + registerfile(L, stdout, "stdout", IO_OUTPUT); + registerfile(L, stderr, "stderr", NULL); + return 1; +} + diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lmathlib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lmathlib.c index 93eb3cba9c..266cc707f7 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lmathlib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lmathlib.c @@ -1,256 +1,256 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include -#include - -#define lmathlib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - -#undef PI -#define PI (3.14159265358979323846) -#define RADIANS_PER_DEGREE (PI/180.0) - - - -/* -** If you want Lua to operate in degrees (instead of radians), -** define USE_DEGREES -*/ -#ifdef USE_DEGREES -#define FROMRAD(a) ((a)/RADIANS_PER_DEGREE) -#define TORAD(a) ((a)*RADIANS_PER_DEGREE) -#else -#define FROMRAD(a) (a) -#define TORAD(a) (a) -#endif - - -static int math_abs (lua_State *L) { - lua_pushnumber(L, fabs(luaL_checknumber(L, 1))); - return 1; -} - -static int math_sin (lua_State *L) { - lua_pushnumber(L, sin(TORAD(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_cos (lua_State *L) { - lua_pushnumber(L, cos(TORAD(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_tan (lua_State *L) { - lua_pushnumber(L, tan(TORAD(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_asin (lua_State *L) { - lua_pushnumber(L, FROMRAD(asin(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_acos (lua_State *L) { - lua_pushnumber(L, FROMRAD(acos(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_atan (lua_State *L) { - lua_pushnumber(L, FROMRAD(atan(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_atan2 (lua_State *L) { - lua_pushnumber(L, FROMRAD(atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2)))); - return 1; -} - -static int math_ceil (lua_State *L) { - lua_pushnumber(L, ceil(luaL_checknumber(L, 1))); - return 1; -} - -static int math_floor (lua_State *L) { - lua_pushnumber(L, floor(luaL_checknumber(L, 1))); - return 1; -} - -static int math_mod (lua_State *L) { - lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); - return 1; -} - -static int math_sqrt (lua_State *L) { - lua_pushnumber(L, sqrt(luaL_checknumber(L, 1))); - return 1; -} - -static int math_pow (lua_State *L) { - lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); - return 1; -} - -static int math_log (lua_State *L) { - lua_pushnumber(L, log(luaL_checknumber(L, 1))); - return 1; -} - -static int math_log10 (lua_State *L) { - lua_pushnumber(L, log10(luaL_checknumber(L, 1))); - return 1; -} - -static int math_exp (lua_State *L) { - lua_pushnumber(L, exp(luaL_checknumber(L, 1))); - return 1; -} - -static int math_deg (lua_State *L) { - lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE); - return 1; -} - -static int math_rad (lua_State *L) { - lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE); - return 1; -} - -static int math_frexp (lua_State *L) { - int e; - lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e)); - lua_pushnumber(L, e); - return 2; -} - -static int math_ldexp (lua_State *L) { - lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2))); - return 1; -} - - - -static int math_min (lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - lua_Number dmin = luaL_checknumber(L, 1); - int i; - for (i=2; i<=n; i++) { - lua_Number d = luaL_checknumber(L, i); - if (d < dmin) - dmin = d; - } - lua_pushnumber(L, dmin); - return 1; -} - - -static int math_max (lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - lua_Number dmax = luaL_checknumber(L, 1); - int i; - for (i=2; i<=n; i++) { - lua_Number d = luaL_checknumber(L, i); - if (d > dmax) - dmax = d; - } - lua_pushnumber(L, dmax); - return 1; -} - - -static int math_random (lua_State *L) { - /* the `%' avoids the (rare) case of r==1, and is needed also because on - some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ - lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX; - switch (lua_gettop(L)) { /* check number of arguments */ - case 0: { /* no arguments */ - lua_pushnumber(L, r); /* Number between 0 and 1 */ - break; - } - case 1: { /* only upper limit */ - int u = luaL_checkint(L, 1); - luaL_argcheck(L, 1<=u, 1, "interval is empty"); - lua_pushnumber(L, (int)floor(r*u)+1); /* int between 1 and `u' */ - break; - } - case 2: { /* lower and upper limits */ - int l = luaL_checkint(L, 1); - int u = luaL_checkint(L, 2); - luaL_argcheck(L, l<=u, 2, "interval is empty"); - lua_pushnumber(L, (int)floor(r*(u-l+1))+l); /* int between `l' and `u' */ - break; - } - default: return luaL_error(L, "wrong number of arguments"); - } - return 1; -} - - -static int math_randomseed (lua_State *L) { - srand(luaL_checkint(L, 1)); - return 0; -} - - -static const luaL_reg mathlib[] = { - {"abs", math_abs}, - {"sin", math_sin}, - {"cos", math_cos}, - {"tan", math_tan}, - {"asin", math_asin}, - {"acos", math_acos}, - {"atan", math_atan}, - {"atan2", math_atan2}, - {"ceil", math_ceil}, - {"floor", math_floor}, - {"mod", math_mod}, - {"frexp", math_frexp}, - {"ldexp", math_ldexp}, - {"sqrt", math_sqrt}, - {"min", math_min}, - {"max", math_max}, - {"log", math_log}, - {"log10", math_log10}, - {"exp", math_exp}, - {"deg", math_deg}, - {"pow", math_pow}, - {"rad", math_rad}, - {"random", math_random}, - {"randomseed", math_randomseed}, - {NULL, NULL} -}; - - -/* -** Open math library -*/ -LUALIB_API int luaopen_math (lua_State *L) { - luaL_openlib(L, LUA_MATHLIBNAME, mathlib, 0); - lua_pushliteral(L, "pi"); - lua_pushnumber(L, PI); - lua_settable(L, -3); - lua_pushliteral(L, "__pow"); - lua_pushcfunction(L, math_pow); - lua_settable(L, LUA_GLOBALSINDEX); - return 1; -} - +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include +#include + +#define lmathlib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#undef PI +#define PI (3.14159265358979323846) +#define RADIANS_PER_DEGREE (PI/180.0) + + + +/* +** If you want Lua to operate in degrees (instead of radians), +** define USE_DEGREES +*/ +#ifdef USE_DEGREES +#define FROMRAD(a) ((a)/RADIANS_PER_DEGREE) +#define TORAD(a) ((a)*RADIANS_PER_DEGREE) +#else +#define FROMRAD(a) (a) +#define TORAD(a) (a) +#endif + + +static int math_abs (lua_State *L) { + lua_pushnumber(L, fabs(luaL_checknumber(L, 1))); + return 1; +} + +static int math_sin (lua_State *L) { + lua_pushnumber(L, sin(TORAD(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_cos (lua_State *L) { + lua_pushnumber(L, cos(TORAD(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_tan (lua_State *L) { + lua_pushnumber(L, tan(TORAD(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_asin (lua_State *L) { + lua_pushnumber(L, FROMRAD(asin(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_acos (lua_State *L) { + lua_pushnumber(L, FROMRAD(acos(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_atan (lua_State *L) { + lua_pushnumber(L, FROMRAD(atan(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_atan2 (lua_State *L) { + lua_pushnumber(L, FROMRAD(atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2)))); + return 1; +} + +static int math_ceil (lua_State *L) { + lua_pushnumber(L, ceil(luaL_checknumber(L, 1))); + return 1; +} + +static int math_floor (lua_State *L) { + lua_pushnumber(L, floor(luaL_checknumber(L, 1))); + return 1; +} + +static int math_mod (lua_State *L) { + lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int math_sqrt (lua_State *L) { + lua_pushnumber(L, sqrt(luaL_checknumber(L, 1))); + return 1; +} + +static int math_pow (lua_State *L) { + lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int math_log (lua_State *L) { + lua_pushnumber(L, log(luaL_checknumber(L, 1))); + return 1; +} + +static int math_log10 (lua_State *L) { + lua_pushnumber(L, log10(luaL_checknumber(L, 1))); + return 1; +} + +static int math_exp (lua_State *L) { + lua_pushnumber(L, exp(luaL_checknumber(L, 1))); + return 1; +} + +static int math_deg (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE); + return 1; +} + +static int math_rad (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE); + return 1; +} + +static int math_frexp (lua_State *L) { + int e; + lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e)); + lua_pushnumber(L, e); + return 2; +} + +static int math_ldexp (lua_State *L) { + lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2))); + return 1; +} + + + +static int math_min (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + lua_Number dmin = luaL_checknumber(L, 1); + int i; + for (i=2; i<=n; i++) { + lua_Number d = luaL_checknumber(L, i); + if (d < dmin) + dmin = d; + } + lua_pushnumber(L, dmin); + return 1; +} + + +static int math_max (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + lua_Number dmax = luaL_checknumber(L, 1); + int i; + for (i=2; i<=n; i++) { + lua_Number d = luaL_checknumber(L, i); + if (d > dmax) + dmax = d; + } + lua_pushnumber(L, dmax); + return 1; +} + + +static int math_random (lua_State *L) { + /* the `%' avoids the (rare) case of r==1, and is needed also because on + some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ + lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX; + switch (lua_gettop(L)) { /* check number of arguments */ + case 0: { /* no arguments */ + lua_pushnumber(L, r); /* Number between 0 and 1 */ + break; + } + case 1: { /* only upper limit */ + int u = luaL_checkint(L, 1); + luaL_argcheck(L, 1<=u, 1, "interval is empty"); + lua_pushnumber(L, (int)floor(r*u)+1); /* int between 1 and `u' */ + break; + } + case 2: { /* lower and upper limits */ + int l = luaL_checkint(L, 1); + int u = luaL_checkint(L, 2); + luaL_argcheck(L, l<=u, 2, "interval is empty"); + lua_pushnumber(L, (int)floor(r*(u-l+1))+l); /* int between `l' and `u' */ + break; + } + default: return luaL_error(L, "wrong number of arguments"); + } + return 1; +} + + +static int math_randomseed (lua_State *L) { + srand(luaL_checkint(L, 1)); + return 0; +} + + +static const luaL_reg mathlib[] = { + {"abs", math_abs}, + {"sin", math_sin}, + {"cos", math_cos}, + {"tan", math_tan}, + {"asin", math_asin}, + {"acos", math_acos}, + {"atan", math_atan}, + {"atan2", math_atan2}, + {"ceil", math_ceil}, + {"floor", math_floor}, + {"mod", math_mod}, + {"frexp", math_frexp}, + {"ldexp", math_ldexp}, + {"sqrt", math_sqrt}, + {"min", math_min}, + {"max", math_max}, + {"log", math_log}, + {"log10", math_log10}, + {"exp", math_exp}, + {"deg", math_deg}, + {"pow", math_pow}, + {"rad", math_rad}, + {"random", math_random}, + {"randomseed", math_randomseed}, + {NULL, NULL} +}; + + +/* +** Open math library +*/ +LUALIB_API int luaopen_math (lua_State *L) { + luaL_openlib(L, LUA_MATHLIBNAME, mathlib, 0); + lua_pushliteral(L, "pi"); + lua_pushnumber(L, PI); + lua_settable(L, -3); + lua_pushliteral(L, "__pow"); + lua_pushcfunction(L, math_pow); + lua_settable(L, LUA_GLOBALSINDEX); + return 1; +} + diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/loadlib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/loadlib.c index 6f4b8bb42d..e28d6018fb 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/loadlib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/loadlib.c @@ -1,205 +1,205 @@ -/* -** $Id: loadlib.c,v 1.1 2005/10/25 09:41:32 vizerie Exp $ -** Dynamic library loader for Lua -** See Copyright Notice in lua.h -* -* This Lua library exports a single function, called loadlib, which is -* called from Lua as loadlib(lib,init), where lib is the full name of the -* library to be loaded (including the complete path) and init is the name -* of a function to be called after the library is loaded. Typically, this -* function will register other functions, thus making the complete library -* available to Lua. The init function is *not* automatically called by -* loadlib. Instead, loadlib returns the init function as a Lua function -* that the client can call when it thinks is appropriate. In the case of -* errors, loadlib returns nil and two strings describing the error. -* The first string is supplied by the operating system; it should be -* informative and useful for error messages. The second string is "open", -* "init", or "absent" to identify the error and is meant to be used for -* making decisions without having to look into the first string (whose -* format is system-dependent). -* -* This module contains an implementation of loadlib for Unix systems that -* have dlfcn, an implementation for Windows, and a stub for other systems. -* See the list at the end of this file for some links to available -* implementations of dlfcn and interfaces to other native dynamic loaders -* on top of which loadlib could be implemented. -* -*/ - -#include "lua.h" -#include "lauxlib.h" -#include "lualib.h" - - -#undef LOADLIB - - -#ifdef USE_DLOPEN -#define LOADLIB -/* -* This is an implementation of loadlib based on the dlfcn interface. -* The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, -* NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least -* as an emulation layer on top of native functions. -*/ - -#include - -static int loadlib(lua_State *L) -{ - const char *path=luaL_checkstring(L,1); - const char *init=luaL_checkstring(L,2); - void *lib=dlopen(path,RTLD_NOW); - if (lib!=NULL) - { - lua_CFunction f=(lua_CFunction) dlsym(lib,init); - if (f!=NULL) - { - lua_pushlightuserdata(L,lib); - lua_pushcclosure(L,f,1); - return 1; - } - } - /* else return appropriate error messages */ - lua_pushnil(L); - lua_pushstring(L,dlerror()); - lua_pushstring(L,(lib!=NULL) ? "init" : "open"); - if (lib!=NULL) dlclose(lib); - return 3; -} - -#endif - - - -/* -** In Windows, default is to use dll; otherwise, default is not to use dll -*/ -#ifndef USE_DLL -#ifdef _WIN32 -#define USE_DLL 1 -#else -#define USE_DLL 0 -#endif -#endif - - -#if USE_DLL -#define LOADLIB -/* -* This is an implementation of loadlib for Windows using native functions. -*/ - -#include - -static void pusherror(lua_State *L) -{ - int error=GetLastError(); - char buffer[128]; - if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, - 0, error, 0, buffer, sizeof(buffer), 0)) - lua_pushstring(L,buffer); - else - lua_pushfstring(L,"system error %d\n",error); -} - -static int loadlib(lua_State *L) -{ - const char *path=luaL_checkstring(L,1); - const char *init=luaL_checkstring(L,2); - HINSTANCE lib=LoadLibrary(path); - if (lib!=NULL) - { - lua_CFunction f=(lua_CFunction) GetProcAddress(lib,init); - if (f!=NULL) - { - lua_pushlightuserdata(L,lib); - lua_pushcclosure(L,f,1); - return 1; - } - } - lua_pushnil(L); - pusherror(L); - lua_pushstring(L,(lib!=NULL) ? "init" : "open"); - if (lib!=NULL) FreeLibrary(lib); - return 3; -} - -#endif - - - -#ifndef LOADLIB -/* Fallback for other systems */ - -/* -** Those systems support dlopen, so they should have defined USE_DLOPEN. -** The default (no)implementation gives them a special error message. -*/ -#ifdef linux -#define LOADLIB -#endif - -#ifdef sun -#define LOADLIB -#endif - -#ifdef sgi -#define LOADLIB -#endif - -#ifdef BSD -#define LOADLIB -#endif - -#ifdef _WIN32 -#define LOADLIB -#endif - -#ifdef LOADLIB -#undef LOADLIB -#define LOADLIB "`loadlib' not installed (check your Lua configuration)" -#else -#define LOADLIB "`loadlib' not supported" -#endif - -static int loadlib(lua_State *L) -{ - lua_pushnil(L); - lua_pushliteral(L,LOADLIB); - lua_pushliteral(L,"absent"); - return 3; -} -#endif - -LUALIB_API int luaopen_loadlib (lua_State *L) -{ - lua_register(L,"loadlib",loadlib); - return 0; -} - -/* -* Here are some links to available implementations of dlfcn and -* interfaces to other native dynamic loaders on top of which loadlib -* could be implemented. Please send contributions and corrections to us. -* -* AIX -* Starting with AIX 4.2, dlfcn is included in the base OS. -* There is also an emulation package available. -* http://www.faqs.org/faqs/aix-faq/part4/section-21.html -* -* HPUX -* HPUX 11 has dlfcn. For HPUX 10 use shl_*. -* http://www.geda.seul.org/mailinglist/geda-dev37/msg00094.html -* http://www.stat.umn.edu/~luke/xls/projects/dlbasics/dlbasics.html -* -* Macintosh, Windows -* http://www.stat.umn.edu/~luke/xls/projects/dlbasics/dlbasics.html -* -* Mac OS X/Darwin -* http://www.opendarwin.org/projects/dlcompat/ -* -* GLIB has wrapper code for BeOS, OS2, Unix and Windows -* http://cvs.gnome.org/lxr/source/glib/gmodule/ -* -*/ +/* +** $Id: loadlib.c,v 1.1 2005/10/25 09:41:32 vizerie Exp $ +** Dynamic library loader for Lua +** See Copyright Notice in lua.h +* +* This Lua library exports a single function, called loadlib, which is +* called from Lua as loadlib(lib,init), where lib is the full name of the +* library to be loaded (including the complete path) and init is the name +* of a function to be called after the library is loaded. Typically, this +* function will register other functions, thus making the complete library +* available to Lua. The init function is *not* automatically called by +* loadlib. Instead, loadlib returns the init function as a Lua function +* that the client can call when it thinks is appropriate. In the case of +* errors, loadlib returns nil and two strings describing the error. +* The first string is supplied by the operating system; it should be +* informative and useful for error messages. The second string is "open", +* "init", or "absent" to identify the error and is meant to be used for +* making decisions without having to look into the first string (whose +* format is system-dependent). +* +* This module contains an implementation of loadlib for Unix systems that +* have dlfcn, an implementation for Windows, and a stub for other systems. +* See the list at the end of this file for some links to available +* implementations of dlfcn and interfaces to other native dynamic loaders +* on top of which loadlib could be implemented. +* +*/ + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + + +#undef LOADLIB + + +#ifdef USE_DLOPEN +#define LOADLIB +/* +* This is an implementation of loadlib based on the dlfcn interface. +* The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, +* NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least +* as an emulation layer on top of native functions. +*/ + +#include + +static int loadlib(lua_State *L) +{ + const char *path=luaL_checkstring(L,1); + const char *init=luaL_checkstring(L,2); + void *lib=dlopen(path,RTLD_NOW); + if (lib!=NULL) + { + lua_CFunction f=(lua_CFunction) dlsym(lib,init); + if (f!=NULL) + { + lua_pushlightuserdata(L,lib); + lua_pushcclosure(L,f,1); + return 1; + } + } + /* else return appropriate error messages */ + lua_pushnil(L); + lua_pushstring(L,dlerror()); + lua_pushstring(L,(lib!=NULL) ? "init" : "open"); + if (lib!=NULL) dlclose(lib); + return 3; +} + +#endif + + + +/* +** In Windows, default is to use dll; otherwise, default is not to use dll +*/ +#ifndef USE_DLL +#ifdef _WIN32 +#define USE_DLL 1 +#else +#define USE_DLL 0 +#endif +#endif + + +#if USE_DLL +#define LOADLIB +/* +* This is an implementation of loadlib for Windows using native functions. +*/ + +#include + +static void pusherror(lua_State *L) +{ + int error=GetLastError(); + char buffer[128]; + if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, + 0, error, 0, buffer, sizeof(buffer), 0)) + lua_pushstring(L,buffer); + else + lua_pushfstring(L,"system error %d\n",error); +} + +static int loadlib(lua_State *L) +{ + const char *path=luaL_checkstring(L,1); + const char *init=luaL_checkstring(L,2); + HINSTANCE lib=LoadLibrary(path); + if (lib!=NULL) + { + lua_CFunction f=(lua_CFunction) GetProcAddress(lib,init); + if (f!=NULL) + { + lua_pushlightuserdata(L,lib); + lua_pushcclosure(L,f,1); + return 1; + } + } + lua_pushnil(L); + pusherror(L); + lua_pushstring(L,(lib!=NULL) ? "init" : "open"); + if (lib!=NULL) FreeLibrary(lib); + return 3; +} + +#endif + + + +#ifndef LOADLIB +/* Fallback for other systems */ + +/* +** Those systems support dlopen, so they should have defined USE_DLOPEN. +** The default (no)implementation gives them a special error message. +*/ +#ifdef linux +#define LOADLIB +#endif + +#ifdef sun +#define LOADLIB +#endif + +#ifdef sgi +#define LOADLIB +#endif + +#ifdef BSD +#define LOADLIB +#endif + +#ifdef _WIN32 +#define LOADLIB +#endif + +#ifdef LOADLIB +#undef LOADLIB +#define LOADLIB "`loadlib' not installed (check your Lua configuration)" +#else +#define LOADLIB "`loadlib' not supported" +#endif + +static int loadlib(lua_State *L) +{ + lua_pushnil(L); + lua_pushliteral(L,LOADLIB); + lua_pushliteral(L,"absent"); + return 3; +} +#endif + +LUALIB_API int luaopen_loadlib (lua_State *L) +{ + lua_register(L,"loadlib",loadlib); + return 0; +} + +/* +* Here are some links to available implementations of dlfcn and +* interfaces to other native dynamic loaders on top of which loadlib +* could be implemented. Please send contributions and corrections to us. +* +* AIX +* Starting with AIX 4.2, dlfcn is included in the base OS. +* There is also an emulation package available. +* http://www.faqs.org/faqs/aix-faq/part4/section-21.html +* +* HPUX +* HPUX 11 has dlfcn. For HPUX 10 use shl_*. +* http://www.geda.seul.org/mailinglist/geda-dev37/msg00094.html +* http://www.stat.umn.edu/~luke/xls/projects/dlbasics/dlbasics.html +* +* Macintosh, Windows +* http://www.stat.umn.edu/~luke/xls/projects/dlbasics/dlbasics.html +* +* Mac OS X/Darwin +* http://www.opendarwin.org/projects/dlcompat/ +* +* GLIB has wrapper code for BeOS, OS2, Unix and Windows +* http://cvs.gnome.org/lxr/source/glib/gmodule/ +* +*/ diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lstrlib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lstrlib.c index 69e40064b9..28cbe0bf42 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lstrlib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lstrlib.c @@ -1,780 +1,780 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include -#include -#include -#include -#include - -#define lstrlib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - -/* macro to `unsign' a character */ -#ifndef uchar -#define uchar(c) ((unsigned char)(c)) -#endif - - -typedef long sint32; /* a signed version for size_t */ - - -static int str_len (lua_State *L) { - size_t l; - luaL_checklstring(L, 1, &l); - lua_pushnumber(L, (lua_Number)l); - return 1; -} - - -static sint32 posrelat (sint32 pos, size_t len) { - /* relative string position: negative means back from end */ - return (pos>=0) ? pos : (sint32)len+pos+1; -} - - -static int str_sub (lua_State *L) { - size_t l; - const char *s = luaL_checklstring(L, 1, &l); - sint32 start = posrelat(luaL_checklong(L, 2), l); - sint32 end = posrelat(luaL_optlong(L, 3, -1), l); - if (start < 1) start = 1; - if (end > (sint32)l) end = (sint32)l; - if (start <= end) - lua_pushlstring(L, s+start-1, end-start+1); - else lua_pushliteral(L, ""); - return 1; -} - - -static int str_lower (lua_State *L) { - size_t l; - size_t i; - luaL_Buffer b; - const char *s = luaL_checklstring(L, 1, &l); - luaL_buffinit(L, &b); - for (i=0; i 0) - luaL_addlstring(&b, s, l); - luaL_pushresult(&b); - return 1; -} - - -static int str_byte (lua_State *L) { - size_t l; - const char *s = luaL_checklstring(L, 1, &l); - sint32 pos = posrelat(luaL_optlong(L, 2, 1), l); - if (pos <= 0 || (size_t)(pos) > l) /* index out of range? */ - return 0; /* no answer */ - lua_pushnumber(L, uchar(s[pos-1])); - return 1; -} - - -static int str_char (lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - int i; - luaL_Buffer b; - luaL_buffinit(L, &b); - for (i=1; i<=n; i++) { - int c = luaL_checkint(L, i); - luaL_argcheck(L, uchar(c) == c, i, "invalid value"); - luaL_putchar(&b, uchar(c)); - } - luaL_pushresult(&b); - return 1; -} - - -static int writer (lua_State *L, const void* b, size_t size, void* B) { - (void)L; - luaL_addlstring((luaL_Buffer*) B, (const char *)b, size); - return 1; -} - - -static int str_dump (lua_State *L) { - luaL_Buffer b; - luaL_checktype(L, 1, LUA_TFUNCTION); - luaL_buffinit(L,&b); - if (!lua_dump(L, writer, &b)) - luaL_error(L, "unable to dump given function"); - luaL_pushresult(&b); - return 1; -} - - - -/* -** {====================================================== -** PATTERN MATCHING -** ======================================================= -*/ - -#ifndef MAX_CAPTURES -#define MAX_CAPTURES 32 /* arbitrary limit */ -#endif - - -#define CAP_UNFINISHED (-1) -#define CAP_POSITION (-2) - -typedef struct MatchState { - const char *src_init; /* init of source string */ - const char *src_end; /* end (`\0') of source string */ - lua_State *L; - int level; /* total number of captures (finished or unfinished) */ - struct { - const char *init; - sint32 len; - } capture[MAX_CAPTURES]; -} MatchState; - - -#define ESC '%' -#define SPECIALS "^$*+?.([%-" - - -static int check_capture (MatchState *ms, int l) { - l -= '1'; - if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) - return luaL_error(ms->L, "invalid capture index"); - return l; -} - - -static int capture_to_close (MatchState *ms) { - int level = ms->level; - for (level--; level>=0; level--) - if (ms->capture[level].len == CAP_UNFINISHED) return level; - return luaL_error(ms->L, "invalid pattern capture"); -} - - -static const char *luaI_classend (MatchState *ms, const char *p) { - switch (*p++) { - case ESC: { - if (*p == '\0') - luaL_error(ms->L, "malformed pattern (ends with `%')"); - return p+1; - } - case '[': { - if (*p == '^') p++; - do { /* look for a `]' */ - if (*p == '\0') - luaL_error(ms->L, "malformed pattern (missing `]')"); - if (*(p++) == ESC && *p != '\0') - p++; /* skip escapes (e.g. `%]') */ - } while (*p != ']'); - return p+1; - } - default: { - return p; - } - } -} - - -static int match_class (int c, int cl) { - int res; - switch (tolower(cl)) { - case 'a' : res = isalpha(c); break; - case 'c' : res = iscntrl(c); break; - case 'd' : res = isdigit(c); break; - case 'l' : res = islower(c); break; - case 'p' : res = ispunct(c); break; - case 's' : res = isspace(c); break; - case 'u' : res = isupper(c); break; - case 'w' : res = isalnum(c); break; - case 'x' : res = isxdigit(c); break; - case 'z' : res = (c == 0); break; - default: return (cl == c); - } - return (islower(cl) ? res : !res); -} - - -static int matchbracketclass (int c, const char *p, const char *ec) { - int sig = 1; - if (*(p+1) == '^') { - sig = 0; - p++; /* skip the `^' */ - } - while (++p < ec) { - if (*p == ESC) { - p++; - if (match_class(c, *p)) - return sig; - } - else if ((*(p+1) == '-') && (p+2 < ec)) { - p+=2; - if (uchar(*(p-2)) <= c && c <= uchar(*p)) - return sig; - } - else if (uchar(*p) == c) return sig; - } - return !sig; -} - - -static int luaI_singlematch (int c, const char *p, const char *ep) { - switch (*p) { - case '.': return 1; /* matches any char */ - case ESC: return match_class(c, *(p+1)); - case '[': return matchbracketclass(c, p, ep-1); - default: return (uchar(*p) == c); - } -} - - -static const char *match (MatchState *ms, const char *s, const char *p); - - -static const char *matchbalance (MatchState *ms, const char *s, - const char *p) { - if (*p == 0 || *(p+1) == 0) - luaL_error(ms->L, "unbalanced pattern"); - if (*s != *p) return NULL; - else { - int b = *p; - int e = *(p+1); - int cont = 1; - while (++s < ms->src_end) { - if (*s == e) { - if (--cont == 0) return s+1; - } - else if (*s == b) cont++; - } - } - return NULL; /* string ends out of balance */ -} - - -static const char *max_expand (MatchState *ms, const char *s, - const char *p, const char *ep) { - sint32 i = 0; /* counts maximum expand for item */ - while ((s+i)src_end && luaI_singlematch(uchar(*(s+i)), p, ep)) - i++; - /* keeps trying to match with the maximum repetitions */ - while (i>=0) { - const char *res = match(ms, (s+i), ep+1); - if (res) return res; - i--; /* else didn't match; reduce 1 repetition to try again */ - } - return NULL; -} - - -static const char *min_expand (MatchState *ms, const char *s, - const char *p, const char *ep) { - for (;;) { - const char *res = match(ms, s, ep+1); - if (res != NULL) - return res; - else if (ssrc_end && luaI_singlematch(uchar(*s), p, ep)) - s++; /* try with one more repetition */ - else return NULL; - } -} - - -static const char *start_capture (MatchState *ms, const char *s, - const char *p, int what) { - const char *res; - int level = ms->level; - if (level >= MAX_CAPTURES) luaL_error(ms->L, "too many captures"); - ms->capture[level].init = s; - ms->capture[level].len = what; - ms->level = level+1; - if ((res=match(ms, s, p)) == NULL) /* match failed? */ - ms->level--; /* undo capture */ - return res; -} - - -static const char *end_capture (MatchState *ms, const char *s, - const char *p) { - int l = capture_to_close(ms); - const char *res; - ms->capture[l].len = s - ms->capture[l].init; /* close capture */ - if ((res = match(ms, s, p)) == NULL) /* match failed? */ - ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ - return res; -} - - -static const char *match_capture (MatchState *ms, const char *s, int l) { - size_t len; - l = check_capture(ms, l); - len = ms->capture[l].len; - if ((size_t)(ms->src_end-s) >= len && - memcmp(ms->capture[l].init, s, len) == 0) - return s+len; - else return NULL; -} - - -static const char *match (MatchState *ms, const char *s, const char *p) { - init: /* using goto's to optimize tail recursion */ - switch (*p) { - case '(': { /* start capture */ - if (*(p+1) == ')') /* position capture? */ - return start_capture(ms, s, p+2, CAP_POSITION); - else - return start_capture(ms, s, p+1, CAP_UNFINISHED); - } - case ')': { /* end capture */ - return end_capture(ms, s, p+1); - } - case ESC: { - switch (*(p+1)) { - case 'b': { /* balanced string? */ - s = matchbalance(ms, s, p+2); - if (s == NULL) return NULL; - p+=4; goto init; /* else return match(ms, s, p+4); */ - } - case 'f': { /* frontier? */ - const char *ep; char previous; - p += 2; - if (*p != '[') - luaL_error(ms->L, "missing `[' after `%%f' in pattern"); - ep = luaI_classend(ms, p); /* points to what is next */ - previous = (s == ms->src_init) ? '\0' : *(s-1); - if (matchbracketclass(uchar(previous), p, ep-1) || - !matchbracketclass(uchar(*s), p, ep-1)) return NULL; - p=ep; goto init; /* else return match(ms, s, ep); */ - } - default: { - if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */ - s = match_capture(ms, s, *(p+1)); - if (s == NULL) return NULL; - p+=2; goto init; /* else return match(ms, s, p+2) */ - } - goto dflt; /* case default */ - } - } - } - case '\0': { /* end of pattern */ - return s; /* match succeeded */ - } - case '$': { - if (*(p+1) == '\0') /* is the `$' the last char in pattern? */ - return (s == ms->src_end) ? s : NULL; /* check end of string */ - else goto dflt; - } - default: dflt: { /* it is a pattern item */ - const char *ep = luaI_classend(ms, p); /* points to what is next */ - int m = ssrc_end && luaI_singlematch(uchar(*s), p, ep); - switch (*ep) { - case '?': { /* optional */ - const char *res; - if (m && ((res=match(ms, s+1, ep+1)) != NULL)) - return res; - p=ep+1; goto init; /* else return match(ms, s, ep+1); */ - } - case '*': { /* 0 or more repetitions */ - return max_expand(ms, s, p, ep); - } - case '+': { /* 1 or more repetitions */ - return (m ? max_expand(ms, s+1, p, ep) : NULL); - } - case '-': { /* 0 or more repetitions (minimum) */ - return min_expand(ms, s, p, ep); - } - default: { - if (!m) return NULL; - s++; p=ep; goto init; /* else return match(ms, s+1, ep); */ - } - } - } - } -} - - - -static const char *lmemfind (const char *s1, size_t l1, - const char *s2, size_t l2) { - if (l2 == 0) return s1; /* empty strings are everywhere */ - else if (l2 > l1) return NULL; /* avoids a negative `l1' */ - else { - const char *init; /* to search for a `*s2' inside `s1' */ - l2--; /* 1st char will be checked by `memchr' */ - l1 = l1-l2; /* `s2' cannot be found after that */ - while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { - init++; /* 1st char is already checked */ - if (memcmp(init, s2+1, l2) == 0) - return init-1; - else { /* correct `l1' and `s1' to try again */ - l1 -= init-s1; - s1 = init; - } - } - return NULL; /* not found */ - } -} - - -static void push_onecapture (MatchState *ms, int i) { - int l = ms->capture[i].len; - if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); - if (l == CAP_POSITION) - lua_pushnumber(ms->L, (lua_Number)(ms->capture[i].init - ms->src_init + 1)); - else - lua_pushlstring(ms->L, ms->capture[i].init, l); -} - - -static int push_captures (MatchState *ms, const char *s, const char *e) { - int i; - luaL_checkstack(ms->L, ms->level, "too many captures"); - if (ms->level == 0 && s) { /* no explicit captures? */ - lua_pushlstring(ms->L, s, e-s); /* return whole match */ - return 1; - } - else { /* return all captures */ - for (i=0; ilevel; i++) - push_onecapture(ms, i); - return ms->level; /* number of strings pushed */ - } -} - - -static int str_find (lua_State *L) { - size_t l1, l2; - const char *s = luaL_checklstring(L, 1, &l1); - const char *p = luaL_checklstring(L, 2, &l2); - sint32 init = posrelat(luaL_optlong(L, 3, 1), l1) - 1; - if (init < 0) init = 0; - else if ((size_t)(init) > l1) init = (sint32)l1; - if (lua_toboolean(L, 4) || /* explicit request? */ - strpbrk(p, SPECIALS) == NULL) { /* or no special characters? */ - /* do a plain search */ - const char *s2 = lmemfind(s+init, l1-init, p, l2); - if (s2) { - lua_pushnumber(L, (lua_Number)(s2-s+1)); - lua_pushnumber(L, (lua_Number)(s2-s+l2)); - return 2; - } - } - else { - MatchState ms; - int anchor = (*p == '^') ? (p++, 1) : 0; - const char *s1=s+init; - ms.L = L; - ms.src_init = s; - ms.src_end = s+l1; - do { - const char *res; - ms.level = 0; - if ((res=match(&ms, s1, p)) != NULL) { - lua_pushnumber(L, (lua_Number)(s1-s+1)); /* start */ - lua_pushnumber(L, (lua_Number)(res-s)); /* end */ - return push_captures(&ms, NULL, 0) + 2; - } - } while (s1++L; - if (lua_isstring(L, 3)) { - const char *news = lua_tostring(L, 3); - size_t l = lua_strlen(L, 3); - size_t i; - for (i=0; i= 3 && (lua_isstring(L, 3) || lua_isfunction(L, 3)), - 3, "string or function expected"); - luaL_buffinit(L, &b); - ms.L = L; - ms.src_init = src; - ms.src_end = src+srcl; - while (n < max_s) { - const char *e; - ms.level = 0; - e = match(&ms, src, p); - if (e) { - n++; - add_s(&ms, &b, src, e); - } - if (e && e>src) /* non empty match? */ - src = e; /* skip it */ - else if (src < ms.src_end) - luaL_putchar(&b, *src++); - else break; - if (anchor) break; - } - luaL_addlstring(&b, src, ms.src_end-src); - luaL_pushresult(&b); - lua_pushnumber(L, (lua_Number)n); /* number of substitutions */ - return 2; -} - -/* }====================================================== */ - - -/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ -#define MAX_ITEM 512 -/* maximum size of each format specification (such as '%-099.99d') */ -#define MAX_FORMAT 20 - - -static void luaI_addquoted (lua_State *L, luaL_Buffer *b, int arg) { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); - luaL_putchar(b, '"'); - while (l--) { - switch (*s) { - case '"': case '\\': case '\n': { - luaL_putchar(b, '\\'); - luaL_putchar(b, *s); - break; - } - case '\0': { - luaL_addlstring(b, "\\000", 4); - break; - } - default: { - luaL_putchar(b, *s); - break; - } - } - s++; - } - luaL_putchar(b, '"'); -} - - -static const char *scanformat (lua_State *L, const char *strfrmt, - char *form, int *hasprecision) { - const char *p = strfrmt; - while (strchr("-+ #0", *p)) p++; /* skip flags */ - if (isdigit(uchar(*p))) p++; /* skip width */ - if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ - if (*p == '.') { - p++; - *hasprecision = 1; - if (isdigit(uchar(*p))) p++; /* skip precision */ - if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ - } - if (isdigit(uchar(*p))) - luaL_error(L, "invalid format (width or precision too long)"); - if (p-strfrmt+2 > MAX_FORMAT) /* +2 to include `%' and the specifier */ - luaL_error(L, "invalid format (too long)"); - form[0] = '%'; - strncpy(form+1, strfrmt, p-strfrmt+1); - form[p-strfrmt+2] = 0; - return p; -} - - -static int str_format (lua_State *L) { - int arg = 1; - size_t sfl; - const char *strfrmt = luaL_checklstring(L, arg, &sfl); - const char *strfrmt_end = strfrmt+sfl; - luaL_Buffer b; - luaL_buffinit(L, &b); - while (strfrmt < strfrmt_end) { - if (*strfrmt != '%') - luaL_putchar(&b, *strfrmt++); - else if (*++strfrmt == '%') - luaL_putchar(&b, *strfrmt++); /* %% */ - else { /* format item */ - char form[MAX_FORMAT]; /* to store the format (`%...') */ - char buff[MAX_ITEM]; /* to store the formatted item */ - int hasprecision = 0; - if (isdigit(uchar(*strfrmt)) && *(strfrmt+1) == '$') - return luaL_error(L, "obsolete option (d$) to `format'"); - arg++; - strfrmt = scanformat(L, strfrmt, form, &hasprecision); - switch (*strfrmt++) { - case 'c': case 'd': case 'i': { - sprintf(buff, form, luaL_checkint(L, arg)); - break; - } - case 'o': case 'u': case 'x': case 'X': { - sprintf(buff, form, (unsigned int)(luaL_checknumber(L, arg))); - break; - } - case 'e': case 'E': case 'f': - case 'g': case 'G': { - sprintf(buff, form, luaL_checknumber(L, arg)); - break; - } - case 'q': { - luaI_addquoted(L, &b, arg); - continue; /* skip the `addsize' at the end */ - } - case 's': { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); - if (!hasprecision && l >= 100) { - /* no precision and string is too long to be formatted; - keep original string */ - lua_pushvalue(L, arg); - luaL_addvalue(&b); - continue; /* skip the `addsize' at the end */ - } - else { - sprintf(buff, form, s); - break; - } - } - default: { /* also treat cases `pnLlh' */ - return luaL_error(L, "invalid option to `format'"); - } - } - luaL_addlstring(&b, buff, strlen(buff)); - } - } - luaL_pushresult(&b); - return 1; -} - - -static const luaL_reg strlib[] = { - {"len", str_len}, - {"sub", str_sub}, - {"lower", str_lower}, - {"upper", str_upper}, - {"char", str_char}, - {"rep", str_rep}, - {"byte", str_byte}, - {"format", str_format}, - {"dump", str_dump}, - {"find", str_find}, - {"gfind", gfind}, - {"gsub", str_gsub}, - {NULL, NULL} -}; - - -/* -** Open string library -*/ -LUALIB_API int luaopen_string (lua_State *L) { - luaL_openlib(L, LUA_STRLIBNAME, strlib, 0); - return 1; -} - +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include +#include +#include +#include +#include + +#define lstrlib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* macro to `unsign' a character */ +#ifndef uchar +#define uchar(c) ((unsigned char)(c)) +#endif + + +typedef long sint32; /* a signed version for size_t */ + + +static int str_len (lua_State *L) { + size_t l; + luaL_checklstring(L, 1, &l); + lua_pushnumber(L, (lua_Number)l); + return 1; +} + + +static sint32 posrelat (sint32 pos, size_t len) { + /* relative string position: negative means back from end */ + return (pos>=0) ? pos : (sint32)len+pos+1; +} + + +static int str_sub (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + sint32 start = posrelat(luaL_checklong(L, 2), l); + sint32 end = posrelat(luaL_optlong(L, 3, -1), l); + if (start < 1) start = 1; + if (end > (sint32)l) end = (sint32)l; + if (start <= end) + lua_pushlstring(L, s+start-1, end-start+1); + else lua_pushliteral(L, ""); + return 1; +} + + +static int str_lower (lua_State *L) { + size_t l; + size_t i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + luaL_buffinit(L, &b); + for (i=0; i 0) + luaL_addlstring(&b, s, l); + luaL_pushresult(&b); + return 1; +} + + +static int str_byte (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + sint32 pos = posrelat(luaL_optlong(L, 2, 1), l); + if (pos <= 0 || (size_t)(pos) > l) /* index out of range? */ + return 0; /* no answer */ + lua_pushnumber(L, uchar(s[pos-1])); + return 1; +} + + +static int str_char (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + luaL_Buffer b; + luaL_buffinit(L, &b); + for (i=1; i<=n; i++) { + int c = luaL_checkint(L, i); + luaL_argcheck(L, uchar(c) == c, i, "invalid value"); + luaL_putchar(&b, uchar(c)); + } + luaL_pushresult(&b); + return 1; +} + + +static int writer (lua_State *L, const void* b, size_t size, void* B) { + (void)L; + luaL_addlstring((luaL_Buffer*) B, (const char *)b, size); + return 1; +} + + +static int str_dump (lua_State *L) { + luaL_Buffer b; + luaL_checktype(L, 1, LUA_TFUNCTION); + luaL_buffinit(L,&b); + if (!lua_dump(L, writer, &b)) + luaL_error(L, "unable to dump given function"); + luaL_pushresult(&b); + return 1; +} + + + +/* +** {====================================================== +** PATTERN MATCHING +** ======================================================= +*/ + +#ifndef MAX_CAPTURES +#define MAX_CAPTURES 32 /* arbitrary limit */ +#endif + + +#define CAP_UNFINISHED (-1) +#define CAP_POSITION (-2) + +typedef struct MatchState { + const char *src_init; /* init of source string */ + const char *src_end; /* end (`\0') of source string */ + lua_State *L; + int level; /* total number of captures (finished or unfinished) */ + struct { + const char *init; + sint32 len; + } capture[MAX_CAPTURES]; +} MatchState; + + +#define ESC '%' +#define SPECIALS "^$*+?.([%-" + + +static int check_capture (MatchState *ms, int l) { + l -= '1'; + if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) + return luaL_error(ms->L, "invalid capture index"); + return l; +} + + +static int capture_to_close (MatchState *ms) { + int level = ms->level; + for (level--; level>=0; level--) + if (ms->capture[level].len == CAP_UNFINISHED) return level; + return luaL_error(ms->L, "invalid pattern capture"); +} + + +static const char *luaI_classend (MatchState *ms, const char *p) { + switch (*p++) { + case ESC: { + if (*p == '\0') + luaL_error(ms->L, "malformed pattern (ends with `%')"); + return p+1; + } + case '[': { + if (*p == '^') p++; + do { /* look for a `]' */ + if (*p == '\0') + luaL_error(ms->L, "malformed pattern (missing `]')"); + if (*(p++) == ESC && *p != '\0') + p++; /* skip escapes (e.g. `%]') */ + } while (*p != ']'); + return p+1; + } + default: { + return p; + } + } +} + + +static int match_class (int c, int cl) { + int res; + switch (tolower(cl)) { + case 'a' : res = isalpha(c); break; + case 'c' : res = iscntrl(c); break; + case 'd' : res = isdigit(c); break; + case 'l' : res = islower(c); break; + case 'p' : res = ispunct(c); break; + case 's' : res = isspace(c); break; + case 'u' : res = isupper(c); break; + case 'w' : res = isalnum(c); break; + case 'x' : res = isxdigit(c); break; + case 'z' : res = (c == 0); break; + default: return (cl == c); + } + return (islower(cl) ? res : !res); +} + + +static int matchbracketclass (int c, const char *p, const char *ec) { + int sig = 1; + if (*(p+1) == '^') { + sig = 0; + p++; /* skip the `^' */ + } + while (++p < ec) { + if (*p == ESC) { + p++; + if (match_class(c, *p)) + return sig; + } + else if ((*(p+1) == '-') && (p+2 < ec)) { + p+=2; + if (uchar(*(p-2)) <= c && c <= uchar(*p)) + return sig; + } + else if (uchar(*p) == c) return sig; + } + return !sig; +} + + +static int luaI_singlematch (int c, const char *p, const char *ep) { + switch (*p) { + case '.': return 1; /* matches any char */ + case ESC: return match_class(c, *(p+1)); + case '[': return matchbracketclass(c, p, ep-1); + default: return (uchar(*p) == c); + } +} + + +static const char *match (MatchState *ms, const char *s, const char *p); + + +static const char *matchbalance (MatchState *ms, const char *s, + const char *p) { + if (*p == 0 || *(p+1) == 0) + luaL_error(ms->L, "unbalanced pattern"); + if (*s != *p) return NULL; + else { + int b = *p; + int e = *(p+1); + int cont = 1; + while (++s < ms->src_end) { + if (*s == e) { + if (--cont == 0) return s+1; + } + else if (*s == b) cont++; + } + } + return NULL; /* string ends out of balance */ +} + + +static const char *max_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + sint32 i = 0; /* counts maximum expand for item */ + while ((s+i)src_end && luaI_singlematch(uchar(*(s+i)), p, ep)) + i++; + /* keeps trying to match with the maximum repetitions */ + while (i>=0) { + const char *res = match(ms, (s+i), ep+1); + if (res) return res; + i--; /* else didn't match; reduce 1 repetition to try again */ + } + return NULL; +} + + +static const char *min_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + for (;;) { + const char *res = match(ms, s, ep+1); + if (res != NULL) + return res; + else if (ssrc_end && luaI_singlematch(uchar(*s), p, ep)) + s++; /* try with one more repetition */ + else return NULL; + } +} + + +static const char *start_capture (MatchState *ms, const char *s, + const char *p, int what) { + const char *res; + int level = ms->level; + if (level >= MAX_CAPTURES) luaL_error(ms->L, "too many captures"); + ms->capture[level].init = s; + ms->capture[level].len = what; + ms->level = level+1; + if ((res=match(ms, s, p)) == NULL) /* match failed? */ + ms->level--; /* undo capture */ + return res; +} + + +static const char *end_capture (MatchState *ms, const char *s, + const char *p) { + int l = capture_to_close(ms); + const char *res; + ms->capture[l].len = s - ms->capture[l].init; /* close capture */ + if ((res = match(ms, s, p)) == NULL) /* match failed? */ + ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ + return res; +} + + +static const char *match_capture (MatchState *ms, const char *s, int l) { + size_t len; + l = check_capture(ms, l); + len = ms->capture[l].len; + if ((size_t)(ms->src_end-s) >= len && + memcmp(ms->capture[l].init, s, len) == 0) + return s+len; + else return NULL; +} + + +static const char *match (MatchState *ms, const char *s, const char *p) { + init: /* using goto's to optimize tail recursion */ + switch (*p) { + case '(': { /* start capture */ + if (*(p+1) == ')') /* position capture? */ + return start_capture(ms, s, p+2, CAP_POSITION); + else + return start_capture(ms, s, p+1, CAP_UNFINISHED); + } + case ')': { /* end capture */ + return end_capture(ms, s, p+1); + } + case ESC: { + switch (*(p+1)) { + case 'b': { /* balanced string? */ + s = matchbalance(ms, s, p+2); + if (s == NULL) return NULL; + p+=4; goto init; /* else return match(ms, s, p+4); */ + } + case 'f': { /* frontier? */ + const char *ep; char previous; + p += 2; + if (*p != '[') + luaL_error(ms->L, "missing `[' after `%%f' in pattern"); + ep = luaI_classend(ms, p); /* points to what is next */ + previous = (s == ms->src_init) ? '\0' : *(s-1); + if (matchbracketclass(uchar(previous), p, ep-1) || + !matchbracketclass(uchar(*s), p, ep-1)) return NULL; + p=ep; goto init; /* else return match(ms, s, ep); */ + } + default: { + if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */ + s = match_capture(ms, s, *(p+1)); + if (s == NULL) return NULL; + p+=2; goto init; /* else return match(ms, s, p+2) */ + } + goto dflt; /* case default */ + } + } + } + case '\0': { /* end of pattern */ + return s; /* match succeeded */ + } + case '$': { + if (*(p+1) == '\0') /* is the `$' the last char in pattern? */ + return (s == ms->src_end) ? s : NULL; /* check end of string */ + else goto dflt; + } + default: dflt: { /* it is a pattern item */ + const char *ep = luaI_classend(ms, p); /* points to what is next */ + int m = ssrc_end && luaI_singlematch(uchar(*s), p, ep); + switch (*ep) { + case '?': { /* optional */ + const char *res; + if (m && ((res=match(ms, s+1, ep+1)) != NULL)) + return res; + p=ep+1; goto init; /* else return match(ms, s, ep+1); */ + } + case '*': { /* 0 or more repetitions */ + return max_expand(ms, s, p, ep); + } + case '+': { /* 1 or more repetitions */ + return (m ? max_expand(ms, s+1, p, ep) : NULL); + } + case '-': { /* 0 or more repetitions (minimum) */ + return min_expand(ms, s, p, ep); + } + default: { + if (!m) return NULL; + s++; p=ep; goto init; /* else return match(ms, s+1, ep); */ + } + } + } + } +} + + + +static const char *lmemfind (const char *s1, size_t l1, + const char *s2, size_t l2) { + if (l2 == 0) return s1; /* empty strings are everywhere */ + else if (l2 > l1) return NULL; /* avoids a negative `l1' */ + else { + const char *init; /* to search for a `*s2' inside `s1' */ + l2--; /* 1st char will be checked by `memchr' */ + l1 = l1-l2; /* `s2' cannot be found after that */ + while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { + init++; /* 1st char is already checked */ + if (memcmp(init, s2+1, l2) == 0) + return init-1; + else { /* correct `l1' and `s1' to try again */ + l1 -= init-s1; + s1 = init; + } + } + return NULL; /* not found */ + } +} + + +static void push_onecapture (MatchState *ms, int i) { + int l = ms->capture[i].len; + if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); + if (l == CAP_POSITION) + lua_pushnumber(ms->L, (lua_Number)(ms->capture[i].init - ms->src_init + 1)); + else + lua_pushlstring(ms->L, ms->capture[i].init, l); +} + + +static int push_captures (MatchState *ms, const char *s, const char *e) { + int i; + luaL_checkstack(ms->L, ms->level, "too many captures"); + if (ms->level == 0 && s) { /* no explicit captures? */ + lua_pushlstring(ms->L, s, e-s); /* return whole match */ + return 1; + } + else { /* return all captures */ + for (i=0; ilevel; i++) + push_onecapture(ms, i); + return ms->level; /* number of strings pushed */ + } +} + + +static int str_find (lua_State *L) { + size_t l1, l2; + const char *s = luaL_checklstring(L, 1, &l1); + const char *p = luaL_checklstring(L, 2, &l2); + sint32 init = posrelat(luaL_optlong(L, 3, 1), l1) - 1; + if (init < 0) init = 0; + else if ((size_t)(init) > l1) init = (sint32)l1; + if (lua_toboolean(L, 4) || /* explicit request? */ + strpbrk(p, SPECIALS) == NULL) { /* or no special characters? */ + /* do a plain search */ + const char *s2 = lmemfind(s+init, l1-init, p, l2); + if (s2) { + lua_pushnumber(L, (lua_Number)(s2-s+1)); + lua_pushnumber(L, (lua_Number)(s2-s+l2)); + return 2; + } + } + else { + MatchState ms; + int anchor = (*p == '^') ? (p++, 1) : 0; + const char *s1=s+init; + ms.L = L; + ms.src_init = s; + ms.src_end = s+l1; + do { + const char *res; + ms.level = 0; + if ((res=match(&ms, s1, p)) != NULL) { + lua_pushnumber(L, (lua_Number)(s1-s+1)); /* start */ + lua_pushnumber(L, (lua_Number)(res-s)); /* end */ + return push_captures(&ms, NULL, 0) + 2; + } + } while (s1++L; + if (lua_isstring(L, 3)) { + const char *news = lua_tostring(L, 3); + size_t l = lua_strlen(L, 3); + size_t i; + for (i=0; i= 3 && (lua_isstring(L, 3) || lua_isfunction(L, 3)), + 3, "string or function expected"); + luaL_buffinit(L, &b); + ms.L = L; + ms.src_init = src; + ms.src_end = src+srcl; + while (n < max_s) { + const char *e; + ms.level = 0; + e = match(&ms, src, p); + if (e) { + n++; + add_s(&ms, &b, src, e); + } + if (e && e>src) /* non empty match? */ + src = e; /* skip it */ + else if (src < ms.src_end) + luaL_putchar(&b, *src++); + else break; + if (anchor) break; + } + luaL_addlstring(&b, src, ms.src_end-src); + luaL_pushresult(&b); + lua_pushnumber(L, (lua_Number)n); /* number of substitutions */ + return 2; +} + +/* }====================================================== */ + + +/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ +#define MAX_ITEM 512 +/* maximum size of each format specification (such as '%-099.99d') */ +#define MAX_FORMAT 20 + + +static void luaI_addquoted (lua_State *L, luaL_Buffer *b, int arg) { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + luaL_putchar(b, '"'); + while (l--) { + switch (*s) { + case '"': case '\\': case '\n': { + luaL_putchar(b, '\\'); + luaL_putchar(b, *s); + break; + } + case '\0': { + luaL_addlstring(b, "\\000", 4); + break; + } + default: { + luaL_putchar(b, *s); + break; + } + } + s++; + } + luaL_putchar(b, '"'); +} + + +static const char *scanformat (lua_State *L, const char *strfrmt, + char *form, int *hasprecision) { + const char *p = strfrmt; + while (strchr("-+ #0", *p)) p++; /* skip flags */ + if (isdigit(uchar(*p))) p++; /* skip width */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + if (*p == '.') { + p++; + *hasprecision = 1; + if (isdigit(uchar(*p))) p++; /* skip precision */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + } + if (isdigit(uchar(*p))) + luaL_error(L, "invalid format (width or precision too long)"); + if (p-strfrmt+2 > MAX_FORMAT) /* +2 to include `%' and the specifier */ + luaL_error(L, "invalid format (too long)"); + form[0] = '%'; + strncpy(form+1, strfrmt, p-strfrmt+1); + form[p-strfrmt+2] = 0; + return p; +} + + +static int str_format (lua_State *L) { + int arg = 1; + size_t sfl; + const char *strfrmt = luaL_checklstring(L, arg, &sfl); + const char *strfrmt_end = strfrmt+sfl; + luaL_Buffer b; + luaL_buffinit(L, &b); + while (strfrmt < strfrmt_end) { + if (*strfrmt != '%') + luaL_putchar(&b, *strfrmt++); + else if (*++strfrmt == '%') + luaL_putchar(&b, *strfrmt++); /* %% */ + else { /* format item */ + char form[MAX_FORMAT]; /* to store the format (`%...') */ + char buff[MAX_ITEM]; /* to store the formatted item */ + int hasprecision = 0; + if (isdigit(uchar(*strfrmt)) && *(strfrmt+1) == '$') + return luaL_error(L, "obsolete option (d$) to `format'"); + arg++; + strfrmt = scanformat(L, strfrmt, form, &hasprecision); + switch (*strfrmt++) { + case 'c': case 'd': case 'i': { + sprintf(buff, form, luaL_checkint(L, arg)); + break; + } + case 'o': case 'u': case 'x': case 'X': { + sprintf(buff, form, (unsigned int)(luaL_checknumber(L, arg))); + break; + } + case 'e': case 'E': case 'f': + case 'g': case 'G': { + sprintf(buff, form, luaL_checknumber(L, arg)); + break; + } + case 'q': { + luaI_addquoted(L, &b, arg); + continue; /* skip the `addsize' at the end */ + } + case 's': { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + if (!hasprecision && l >= 100) { + /* no precision and string is too long to be formatted; + keep original string */ + lua_pushvalue(L, arg); + luaL_addvalue(&b); + continue; /* skip the `addsize' at the end */ + } + else { + sprintf(buff, form, s); + break; + } + } + default: { /* also treat cases `pnLlh' */ + return luaL_error(L, "invalid option to `format'"); + } + } + luaL_addlstring(&b, buff, strlen(buff)); + } + } + luaL_pushresult(&b); + return 1; +} + + +static const luaL_reg strlib[] = { + {"len", str_len}, + {"sub", str_sub}, + {"lower", str_lower}, + {"upper", str_upper}, + {"char", str_char}, + {"rep", str_rep}, + {"byte", str_byte}, + {"format", str_format}, + {"dump", str_dump}, + {"find", str_find}, + {"gfind", gfind}, + {"gsub", str_gsub}, + {NULL, NULL} +}; + + +/* +** Open string library +*/ +LUALIB_API int luaopen_string (lua_State *L) { + luaL_openlib(L, LUA_STRLIBNAME, strlib, 0); + return 1; +} + diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ltablib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ltablib.c index 50fd282386..2c18bc7f7e 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ltablib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ltablib.c @@ -1,260 +1,260 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include - -#define ltablib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - -#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n)) - - -static int luaB_foreachi (lua_State *L) { - int i; - int n = aux_getn(L, 1); - luaL_checktype(L, 2, LUA_TFUNCTION); - for (i=1; i<=n; i++) { - lua_pushvalue(L, 2); /* function */ - lua_pushnumber(L, (lua_Number)i); /* 1st argument */ - lua_rawgeti(L, 1, i); /* 2nd argument */ - lua_call(L, 2, 1); - if (!lua_isnil(L, -1)) - return 1; - lua_pop(L, 1); /* remove nil result */ - } - return 0; -} - - -static int luaB_foreach (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - luaL_checktype(L, 2, LUA_TFUNCTION); - lua_pushnil(L); /* first key */ - for (;;) { - if (lua_next(L, 1) == 0) - return 0; - lua_pushvalue(L, 2); /* function */ - lua_pushvalue(L, -3); /* key */ - lua_pushvalue(L, -3); /* value */ - lua_call(L, 2, 1); - if (!lua_isnil(L, -1)) - return 1; - lua_pop(L, 2); /* remove value and result */ - } -} - - -static int luaB_getn (lua_State *L) { - lua_pushnumber(L, (lua_Number)aux_getn(L, 1)); - return 1; -} - - -static int luaB_setn (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - luaL_setn(L, 1, luaL_checkint(L, 2)); - return 0; -} - - -static int luaB_tinsert (lua_State *L) { - int v = lua_gettop(L); /* number of arguments */ - int n = aux_getn(L, 1) + 1; - int pos; /* where to insert new element */ - if (v == 2) /* called with only 2 arguments */ - pos = n; /* insert new element at the end */ - else { - pos = luaL_checkint(L, 2); /* 2nd argument is the position */ - if (pos > n) n = pos; /* `grow' array if necessary */ - v = 3; /* function may be called with more than 3 args */ - } - luaL_setn(L, 1, n); /* new size */ - while (--n >= pos) { /* move up elements */ - lua_rawgeti(L, 1, n); - lua_rawseti(L, 1, n+1); /* t[n+1] = t[n] */ - } - lua_pushvalue(L, v); - lua_rawseti(L, 1, pos); /* t[pos] = v */ - return 0; -} - - -static int luaB_tremove (lua_State *L) { - int n = aux_getn(L, 1); - int pos = luaL_optint(L, 2, n); - if (n <= 0) return 0; /* table is `empty' */ - luaL_setn(L, 1, n-1); /* t.n = n-1 */ - lua_rawgeti(L, 1, pos); /* result = t[pos] */ - for ( ;pos= P */ - while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { - if (i>u) luaL_error(L, "invalid order function for sorting"); - lua_pop(L, 1); /* remove a[i] */ - } - /* repeat --j until a[j] <= P */ - while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { - if (j +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include + +#define ltablib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n)) + + +static int luaB_foreachi (lua_State *L) { + int i; + int n = aux_getn(L, 1); + luaL_checktype(L, 2, LUA_TFUNCTION); + for (i=1; i<=n; i++) { + lua_pushvalue(L, 2); /* function */ + lua_pushnumber(L, (lua_Number)i); /* 1st argument */ + lua_rawgeti(L, 1, i); /* 2nd argument */ + lua_call(L, 2, 1); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 1); /* remove nil result */ + } + return 0; +} + + +static int luaB_foreach (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checktype(L, 2, LUA_TFUNCTION); + lua_pushnil(L); /* first key */ + for (;;) { + if (lua_next(L, 1) == 0) + return 0; + lua_pushvalue(L, 2); /* function */ + lua_pushvalue(L, -3); /* key */ + lua_pushvalue(L, -3); /* value */ + lua_call(L, 2, 1); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 2); /* remove value and result */ + } +} + + +static int luaB_getn (lua_State *L) { + lua_pushnumber(L, (lua_Number)aux_getn(L, 1)); + return 1; +} + + +static int luaB_setn (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_setn(L, 1, luaL_checkint(L, 2)); + return 0; +} + + +static int luaB_tinsert (lua_State *L) { + int v = lua_gettop(L); /* number of arguments */ + int n = aux_getn(L, 1) + 1; + int pos; /* where to insert new element */ + if (v == 2) /* called with only 2 arguments */ + pos = n; /* insert new element at the end */ + else { + pos = luaL_checkint(L, 2); /* 2nd argument is the position */ + if (pos > n) n = pos; /* `grow' array if necessary */ + v = 3; /* function may be called with more than 3 args */ + } + luaL_setn(L, 1, n); /* new size */ + while (--n >= pos) { /* move up elements */ + lua_rawgeti(L, 1, n); + lua_rawseti(L, 1, n+1); /* t[n+1] = t[n] */ + } + lua_pushvalue(L, v); + lua_rawseti(L, 1, pos); /* t[pos] = v */ + return 0; +} + + +static int luaB_tremove (lua_State *L) { + int n = aux_getn(L, 1); + int pos = luaL_optint(L, 2, n); + if (n <= 0) return 0; /* table is `empty' */ + luaL_setn(L, 1, n-1); /* t.n = n-1 */ + lua_rawgeti(L, 1, pos); /* result = t[pos] */ + for ( ;pos= P */ + while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (i>u) luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[i] */ + } + /* repeat --j until a[j] <= P */ + while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { + if (j */ -body { background: #FFFFFF; color: #000000; margin-left: 20px; margin-right: 20px; } -body { background: #FFFFFF; color: #000000; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 70%; margin: 0; } -caption, a.elref, a.elref, caption { font-weight: bold; } -div.ah { background-color: #000000; color: #FFFFFF; font-weight: bold; margin-bottom: 3px; margin-top: 3px; } -div.ah{ background: #000000; color: #FFFFFF; font-weight: bold; margin-bottom: 3px; margin-top: 3px; } -div.footer{ background: #B0C2D7; border: 1px solid #436976; color: #000000; font-style: normal; margin-top: 5em; padding-bottom: 3px; text-align: center; } -div.fragment { background-color: #F5F5F5; border: 1px solid #CCCCCC; margin: 4px; padding-left: 4px; width: 98%; } -div.fragment{ background: #FFFFCC; border: 1px solid #CCCCCC; margin-left: 2em; margin-right: 2em; } -pre { font-size: 120%; margin:2px 1px 3px 1px; } -div.groupheader { font-weight: bold; margin-bottom: 6px; margin-left: 16px; margin-top: 12px; } -div.groupheader{ border-bottom: 1px solid #557E8C; font-weight: bold; margin-bottom: 6px; margin-left: 16px; margin-top: 12px; } -div.grouptext{ font-style: italic; margin-left: 16px; } -div.header{ background: #B0C2D7; border: 1px solid #436976; color: #000000; font-style: normal; margin-bottom: 1em; padding-bottom: 3px; padding-left: 10px; text-align: left; text-transform: lowercase; } -div.header a, div.footer a{ color: #436976; font-weight: bold; white-space: nowrap; } -div.header a {margin-right: 1em; } -div.header a:visited, div.footer a:visited { color: #436976; font-weight: bold; } -div.header a:hover, div.footer a:hover { color: #B52010; font-weight: bold; text-decoration: none; } -div.index { padding-right: 10px; text-align: right; } -div.maindox { margin: 2%; width: 96%; } -div.nav { float: left; } -div.qindex { background-color: #EEEEFF; border: 4px solid #EEEEFF; margin-bottom: 2px; text-align: center; width: 100%; } -dl.el { margin-left: -1cm; } -font.charliteral, span.charliteral { color: #008080; } -font.comment, span.comment { color: #800000; } -font.keyword, span.keyword { color: #008000; } -font.keywordflow, span.keywordflow { color: #E08000; } -font.keywordtype, span.keywordtype { color: #604020; } -font.preprocessor, span.preprocessor { color: #806020; } -font.stringliteral, span.stringliteral { color: #002080; } -h1{ font-size: 220%; text-align: center; } -h2{ border-bottom: 2px solid #557E8C; font-size: 170%; } -hr { border: 1px solid #000000; } -img.formuladsp { } -img.formulainl { vertical-align: middle; } -p.formuladsp { text-align: center; } -td { font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 70%; } -td.indexkey{ background: #EEEEFF; font-weight: bold; margin: 2px 0px; padding: 2px 10px; } -td.indexvalue{ background: #EEEEFF; font-style: italic; margin: 2px 0px; padding: 2px 10px; } -td.md{ font-weight: bold; } -td.mdname, td.mdname1 { color: #FF9900; font-weight: bold; } -tr.memlist { background-color: #F0F0F0; } -ul { list-style-type: square; } -.mdescleft { background-color: #FAFAFA; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-top: 1px none #E0E0E0; margin: 0px; padding-left: 8px; } -.mdescright { background-color: #FAFAFA; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-top: 1px none #E0E0E0; font-style: italic; margin: 0px; padding-bottom: 0px; padding-left: 4px; padding-right: 8px; } -.mdrow { padding: 8px 20px; } -.mdtable { background: #EEEEEE; border: 1px solid #CCCCCC; } -.memitemleft { background-color: #FAFAFA; border-top-color: #FFFFFF; border-top-style: solid; border-top-width: 2px; font-family: Geneva, Arial, Helvetica, sans-serif; font-size: 12px; margin: 4px; padding: 1px 0px 0px 8px; } -.memitemright { background-color: #FAFAFA; border-top-color: #FFFFFF; border-top-style: solid; border-top-width: 2px; font-family: Geneva, Arial, Helvetica, sans-serif; font-size: 13px; margin: 4px; padding: 1px 0px 0px 8px; } +a.code { color: #4444EE; font-weight: normal; text-decoration: none; } +a.coderef { color: #4444EE; font-weight: normal; } +a:link { color: #0066CC; text-decoration: none; } +a:visited { color: #436976; text-decoration: none; } +a.code{ color: #436976; font-weight: normal; text-decoration: none; } +a.coderef{ color: #436976; font-weight: normal; } +a.qindex{ font-weight: normal; white-space: nowrap; } +a.qindex:hover, a.qindexref:hover { background-color: #DDDDFF; text-decoration: none; } +a.qindexhl, a.qindexrefhl { background-color: #6666CC; color: #FFFFFF; font-weight: bold; text-decoration: none; } +a.qindexhl:hover, a.qindexrefhl:hover { background-color: #6666CC; text-decoration: none; } +a.qindexref{ } +a:hover { color: #B52010; color: #B52010; text-decoration: underline; text-decoration: underline; } +a.el, a.qindex, a.qindexref, a.el { font-weight: bold; text-decoration: none; } +/* this is repeated to fix a difference in browser rendering, perhaps should be set for each class of */ +body { background: #FFFFFF; color: #000000; margin-left: 20px; margin-right: 20px; } +body { background: #FFFFFF; color: #000000; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 70%; margin: 0; } +caption, a.elref, a.elref, caption { font-weight: bold; } +div.ah { background-color: #000000; color: #FFFFFF; font-weight: bold; margin-bottom: 3px; margin-top: 3px; } +div.ah{ background: #000000; color: #FFFFFF; font-weight: bold; margin-bottom: 3px; margin-top: 3px; } +div.footer{ background: #B0C2D7; border: 1px solid #436976; color: #000000; font-style: normal; margin-top: 5em; padding-bottom: 3px; text-align: center; } +div.fragment { background-color: #F5F5F5; border: 1px solid #CCCCCC; margin: 4px; padding-left: 4px; width: 98%; } +div.fragment{ background: #FFFFCC; border: 1px solid #CCCCCC; margin-left: 2em; margin-right: 2em; } +pre { font-size: 120%; margin:2px 1px 3px 1px; } +div.groupheader { font-weight: bold; margin-bottom: 6px; margin-left: 16px; margin-top: 12px; } +div.groupheader{ border-bottom: 1px solid #557E8C; font-weight: bold; margin-bottom: 6px; margin-left: 16px; margin-top: 12px; } +div.grouptext{ font-style: italic; margin-left: 16px; } +div.header{ background: #B0C2D7; border: 1px solid #436976; color: #000000; font-style: normal; margin-bottom: 1em; padding-bottom: 3px; padding-left: 10px; text-align: left; text-transform: lowercase; } +div.header a, div.footer a{ color: #436976; font-weight: bold; white-space: nowrap; } +div.header a {margin-right: 1em; } +div.header a:visited, div.footer a:visited { color: #436976; font-weight: bold; } +div.header a:hover, div.footer a:hover { color: #B52010; font-weight: bold; text-decoration: none; } +div.index { padding-right: 10px; text-align: right; } +div.maindox { margin: 2%; width: 96%; } +div.nav { float: left; } +div.qindex { background-color: #EEEEFF; border: 4px solid #EEEEFF; margin-bottom: 2px; text-align: center; width: 100%; } +dl.el { margin-left: -1cm; } +font.charliteral, span.charliteral { color: #008080; } +font.comment, span.comment { color: #800000; } +font.keyword, span.keyword { color: #008000; } +font.keywordflow, span.keywordflow { color: #E08000; } +font.keywordtype, span.keywordtype { color: #604020; } +font.preprocessor, span.preprocessor { color: #806020; } +font.stringliteral, span.stringliteral { color: #002080; } +h1{ font-size: 220%; text-align: center; } +h2{ border-bottom: 2px solid #557E8C; font-size: 170%; } +hr { border: 1px solid #000000; } +img.formuladsp { } +img.formulainl { vertical-align: middle; } +p.formuladsp { text-align: center; } +td { font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 70%; } +td.indexkey{ background: #EEEEFF; font-weight: bold; margin: 2px 0px; padding: 2px 10px; } +td.indexvalue{ background: #EEEEFF; font-style: italic; margin: 2px 0px; padding: 2px 10px; } +td.md{ font-weight: bold; } +td.mdname, td.mdname1 { color: #FF9900; font-weight: bold; } +tr.memlist { background-color: #F0F0F0; } +ul { list-style-type: square; } +.mdescleft { background-color: #FAFAFA; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-top: 1px none #E0E0E0; margin: 0px; padding-left: 8px; } +.mdescright { background-color: #FAFAFA; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-top: 1px none #E0E0E0; font-style: italic; margin: 0px; padding-bottom: 0px; padding-left: 4px; padding-right: 8px; } +.mdrow { padding: 8px 20px; } +.mdtable { background: #EEEEEE; border: 1px solid #CCCCCC; } +.memitemleft { background-color: #FAFAFA; border-top-color: #FFFFFF; border-top-style: solid; border-top-width: 2px; font-family: Geneva, Arial, Helvetica, sans-serif; font-size: 12px; margin: 4px; padding: 1px 0px 0px 8px; } +.memitemright { background-color: #FAFAFA; border-top-color: #FFFFFF; border-top-style: solid; border-top-width: 2px; font-family: Geneva, Arial, Helvetica, sans-serif; font-size: 13px; margin: 4px; padding: 1px 0px 0px 8px; } From c435892d2fa3bd16c8660d0845dcb2ade2975725 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 20:12:02 +0000 Subject: [PATCH 07/24] Implement final decal design from PDF: clip(), batched rendering, UV generation, clipping modes Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/include/nel/3d/decal.h | 194 +++++++------ nel/include/nel/3d/decal_manager.h | 98 ++++--- nel/include/nel/3d/render_trav.h | 2 - nel/include/nel/3d/u_decal.h | 44 +-- nel/src/3d/decal.cpp | 394 ++++++++++++++------------- nel/src/3d/decal_manager.cpp | 243 ++++++++++------- nel/src/3d/u_decal.cpp | 75 +++++ nel/src/3d/visual_collision_mesh.cpp | 8 +- 8 files changed, 601 insertions(+), 457 deletions(-) create mode 100644 nel/src/3d/u_decal.cpp diff --git a/nel/include/nel/3d/decal.h b/nel/include/nel/3d/decal.h index 73687a294b..90f4bb9d0b 100644 --- a/nel/include/nel/3d/decal.h +++ b/nel/include/nel/3d/decal.h @@ -1,7 +1,9 @@ /** \file decal.h - * + * Projected texture decal system for NeL 3D. * - * $Id$ + * Based on the design from the intern report by Christopher Tarento (2007). + * Decals project textures onto scene geometry using a unit-cube bounding box, + * quad-grid face selection, and batched rendering through CDecalManager. */ /* Copyright, 2007 Nevrax Ltd. @@ -30,7 +32,6 @@ #include "nel/3d/transform.h" #include "nel/3d/material.h" -#include "nel/3d/shadow_map.h" #include "nel/3d/vertex_buffer.h" #include "nel/3d/index_buffer.h" #include "nel/3d/u_driver.h" @@ -50,8 +51,6 @@ namespace NL3D { class CScene; -class CShadowMap; -class CShadowPolyReceiver; class CDecalManager; class UScene; @@ -62,9 +61,23 @@ class UDriver; const NLMISC::CClassId DecalId=NLMISC::CClassId(0x6a570fe6, 0x32323e16); +// *************************************************************************** +/// Clipping mode for decal face selection (see PDF §4.5.2) +enum TDecalClipMode +{ + /// No clipping: selected faces are used as-is + DecalClipNone = 0, + /// Mask clipping: use a second texture stage as a mask to delimit edges (preserves geometry, saves CPU) + DecalClipMask, + /// Geometry clipping: clip vertices against bounding box planes (saves fillrate, costs CPU) + DecalClipGeometry +}; + + // *************************************************************************** /** - * Decals are render on mesh + * Context for decal face selection through visual collision. + * Carries clip planes, bounding box, and destination triangle list. * \author Christopher Tarento * \author Nevrax France * \date 2007 @@ -80,12 +93,21 @@ class CDecalContext NLMISC::CPolygon2D Poly2D; CMatrix WorldMatrix; std::vector *DestTris; - bool Clipping; + TDecalClipMode ClipMode; }; +// *************************************************************************** /** - * Decals are render on mesh + * A projected texture decal in the scene graph. + * + * The decal is a CTransform-based model with a unit-cube projection volume. + * It overrides traverseClip() for bounding-sphere frustum culling (§.6.1) + * and traverseRender() to register with CDecalManager for batched rendering. + * + * Face selection uses quad-grid + clip-plane refinement via CVisualCollisionMesh. + * UV coordinates are generated from the inverse world matrix of the unit cube (§4.5.3). + * * \author Christopher Tarento * \author Nevrax France * \date 2007 @@ -93,118 +115,128 @@ class CDecalContext class CDecal : public CTransform { public: - /** Constructor - */ + /// Constructor CDecal(); - /** Destructor - */ + /// Destructor ~CDecal(); - /** Initialization - * Implementation for Ctransform - */ + /// Initialization after insertion in scene graph. void initModel(); - /** Registering decal as valid model - * Needed for scene auto registering process - */ + /// Register CDecal as a valid model type for scene auto-registration. static void registerBasic(); - /** Render traversal - * No-op. Add this decal to the decal manager + /** Clip method override. + * Uses bounding sphere vs frustum test for fast culling (see PDF §.6.1). + * \return true if the decal is visible + */ + bool clip(); + + /** Render traversal. + * Registers this decal with the CDecalManager for batched rendering. */ void traverseRender(); - /** Get Material - * \return decal material + /// Get the decal's material (for texture/blend setup). + CMaterial &getMaterial() { return _Mat; } + + /** Get the decal's material ID for batching. + * Decals sharing the same material ID are batched together in the manager. */ - CMaterial &getMaterial(){return _Mat;} - - /** Get Vertices - * Recompute vertices if needed and return update vector - * \return vector of vertices ready to be rendered + uint32 getMaterialId() const { return _MaterialId; } + + /** Set the decal's material ID. + * This ID is assigned when registering a material with CDecalManager. */ - std::vector &getVertices(const bool useVertexProgram); - - /** Set UV coordinate - * Allow decal to share a single texture - * \param v1 (0,0) corner - * \param v2 (1,1) corner + void setMaterialId(uint32 id) { _MaterialId = id; } + + /** Set the decal texture from a filename. + * \param filename Path to the texture file. */ - void setUVCoord(const CUV uv1, const CUV uv2); + void setTexture(const std::string &filename); + + /** Get vertices and UVs for rendering. + * Recomputes if the decal is touched (moved or camera moved beyond threshold). + * \return vector of vertices (position interleaved, 3 per triangle) + */ + std::vector &getVertices(const bool useVertexProgram); - /** Enable decal Clipping - * Rendering is direct or after processing clipping - * \param clip enable or disable clipping + /** Get UV coordinates corresponding to the vertices. + * Valid after calling getVertices(). + * \return vector of UV coordinates (one per vertex) */ - void enableClipping(const bool clip){_DecalContext.Clipping = clip;} + const std::vector &getUVs() const { return _UVs; } - /** Indicates if this decal is a static one - * Allow to manager specific optimizations - * \param isStatic value + /** Set UV sub-region within a texture atlas. + * \param uv1 Top-left UV coordinate + * \param uv2 Bottom-right UV coordinate */ - void setStatic(const bool isStatic){_IsStatic = isStatic;} - + void setUVCoord(const CUV uv1, const CUV uv2); -private: - /** Creator - * Used when we add a decal to the scene + /** Set the clipping mode for face selection. + * \param mode One of DecalClipNone, DecalClipMask, DecalClipGeometry */ - static CTransform *creator() {return new CDecal();} - - /** Compute decal collision and projection on shape - * Called by getVertices - * \param useVertexProgram Indicate if we must use vertex program for rendering + void setClippingMode(TDecalClipMode mode) { _DecalContext.ClipMode = mode; } + + /// Get the current clipping mode. + TDecalClipMode getClippingMode() const { return _DecalContext.ClipMode; } + + /** Mark this decal as static for caching optimization. + * Static decals only recompute geometry when first created or when + * the camera moves beyond the visibility distance threshold (see PDF §4.5.4). + * \param isStatic true for static decals */ - void computeDecal(const bool useVertexProgram); + void setStatic(const bool isStatic) { _IsStatic = isStatic; } - ///////////////////////////////////////////////////////////// + /// Return whether this decal is marked static. + bool isStatic() const { return _IsStatic; } -public: - /** Get the Matrix that transforms local coordinates in uv coordinates - * matrix to map (x,y) = (0,0) to (u,v) = (0,1) & (x,y) = (0,1) to (u,v) = (0,0) in local decal space - * \return reverse Matrix + /** Get the Matrix that transforms local coordinates to UV coordinates. + * Maps (x,y)=(0,0) to (u,v)=(0,1) and (x,y)=(0,1) to (u,v)=(0,0) + * in local decal space (unit cube). See PDF §4.5.3. */ static CMatrix getReverseUVMatrix() { CMatrix m; - m.setRot(CVector::I,-CVector::J,CVector::K); + m.setRot(CVector::I, -CVector::J, CVector::K); m.setPos(CVector::J); return m; - }; + } private: - CVertexBuffer _Vb; - - CIndexBuffer _Ib; - - CMaterial _Mat; - /////////////////////////////////////////////////////////////// + /// Creator function for scene model registration. + static CTransform *creator() { return new CDecal(); } + + /** Compute decal geometry: face selection, clipping, and UV generation. + * Called by getVertices() when the decal needs recomputation. + */ + void computeDecal(const bool useVertexProgram); + + /** Generate UV coordinates for the collected vertices. + * Uses the inverse world matrix to project back to unit-cube local space, + * then maps to UV sub-region defined by _UV1/_UV2. See PDF §4.5.3. + * Camera position is subtracted for numerical stability (§4.6.1). + */ + void generateUVs(); + +private: + CMaterial _Mat; + uint32 _MaterialId; + bool _Touched; - + uint32 _StableFrameCount; + CVector _LastCamPos; CVector _ClipCorners[4]; - CVector _RefPosition; - float _WorldToUVMatrix[4][4]; - CMatrix _InvertedWorldMatrix; - CMatrix _TextureMatrix; - float _WorldMatrixFlat[16]; - - CShadowMap *_ShadowMap; - - - CVertexBuffer _VB; - bool _VBInitialized; + std::vector _Vertices; + std::vector _UVs; bool _IsStatic; - + CUV _UV1; CUV _UV2; CDecalContext _DecalContext; - ////////////////////////////////////////////////////////////// - - }; }//NL3D diff --git a/nel/include/nel/3d/decal_manager.h b/nel/include/nel/3d/decal_manager.h index 7a86144586..03976e59c0 100644 --- a/nel/include/nel/3d/decal_manager.h +++ b/nel/include/nel/3d/decal_manager.h @@ -1,7 +1,8 @@ /** \file decal_manager.h - * + * Batched decal rendering manager for NeL 3D. * - * $Id$ + * Collects decals sorted by material and renders them in batched draw calls + * using a fixed-size AGP volatile vertex buffer. See PDF §4.5.4 and §.6.3. */ /* Copyright, 2007 Nevrax Ltd. @@ -26,36 +27,34 @@ #ifndef NL_DECAL_MANAGER_H #define NL_DECAL_MANAGER_H -#include - #include "nel/3d/transform.h" #include "nel/3d/decal.h" #include "nel/3d/material.h" #include "nel/3d/vertex_buffer.h" -#include "nel/3d/index_buffer.h" #include "nel/3d/driver.h" - - -namespace NLMISC -{ - class CPlane; -} - - namespace NL3D { class CScene; +/// Maximum number of vertices in the batching array buffer (see PDF §4.5.4). +/// When exceeded, the buffer is flushed and refilled. +const uint32 NL3D_DECAL_VB_MAX_VERTICES = 4096 * 3; // *************************************************************************** /** - * Decal Manager contains decal and computes batch rendering + * Decal Manager: collects decals per material and renders them in batched draw calls. + * + * Each frame, CDecal::traverseRender() registers decals here. At flush time, + * the manager iterates per-material groups, fills a fixed-size AGP volatile + * vertex buffer (position + UV), and issues renderRawTriangles() calls. + * If the buffer overflows mid-decal, it is flushed and refilled (see PDF §.6.3). + * * \author Christopher Tarento * \author Nevrax France * \date 2007 @@ -64,58 +63,55 @@ class CDecalManager { public: - /** Constructor - */ CDecalManager(); - - /** Destructor - */ ~CDecalManager(); - - /** Render all decals - * \param sc Owner scene + + /** Render all registered decals in batched draw calls. + * Iterates per-material, fills the VB, and renders. See PDF §.6.3. + * \param sc Owner scene (for driver access) */ void flush(CScene *sc); - - /** Add a decal to the manager - * \param decal Decal to add to the manager - * \param texName Name of the decal's texture - */ - void addDecal(CDecal *decal, const std::string &texName); - /** Clear all the decals of the manager - * + /** Register a decal for rendering this frame. + * \param decal The decal to add + * \param materialId Material ID for batching (decals with same ID are grouped) */ - void clearAllDecals(); + void addDecal(CDecal *decal, uint32 materialId); - /** Indicates if we must recompute decals position - * \param b true or false + /** Register a material for use by decals. + * \param mat The material to register + * \return The material ID to use when creating decals */ - void setTouched(const bool b){_Touched = b;} + uint32 registerMaterial(const CMaterial &mat); - /** Indicates if we must use vertex program for rendering - * \param b true or false - */ - void setVertexProgram(const bool b){_UseVertexProgram = b;} + /// Clear all registered decals (called at start of each frame). + void clearAllDecals(); -private: - ///tmp - void computeBatch(); - void renderStatic(); - void renderDynamic(); + /** Set whether vertex programs should be used. + * \param b true to use vertex programs + */ + void setVertexProgram(const bool b) { _UseVertexProgram = b; } private: - typedef CHashMap > TDecalMap; + /// A registered material with its ID + struct CRegisteredMaterial + { + CMaterial Mat; + uint32 Id; + }; + + /// Decals grouped by material ID + typedef std::map > TDecalMap; TDecalMap _Decals; + /// Registered materials by ID + std::vector _Materials; + uint32 _NextMaterialId; + bool _UseVertexProgram; - - std::vector _StaticDecals; - std::vector _DynamicDecals; - CVertexBuffer _VBRaw;//static, sorted by material - CVertexBuffer _VB;//dynamic - uint _UsedVertices; - bool _Touched;///someone has moved ? + + /// Fixed-size AGP volatile vertex buffer (Position + TexCoord0) + CVertexBuffer _VB; }; diff --git a/nel/include/nel/3d/render_trav.h b/nel/include/nel/3d/render_trav.h index 7bf31ec2df..9c6403b469 100644 --- a/nel/include/nel/3d/render_trav.h +++ b/nel/include/nel/3d/render_trav.h @@ -256,8 +256,6 @@ class CRenderTrav : public CTravCameraScene // add a landscape. Special for CLandscapeModel::traverseRender(); void addRenderLandscape(CLandscapeModel *model); - // add a decal. Special for CDecal::traverseRender(); - void addRenderDecal(CDecal *decal); /// \name Temp Debug diff --git a/nel/include/nel/3d/u_decal.h b/nel/include/nel/3d/u_decal.h index 80649f0128..2d09045663 100644 --- a/nel/include/nel/3d/u_decal.h +++ b/nel/include/nel/3d/u_decal.h @@ -1,10 +1,8 @@ /** \file u_decal.h - * TODO: File description - * - * $Id$ + * User interface for projected texture decals. */ -/* Copyright, 2001 Nevrax Ltd. +/* Copyright, 2007 Nevrax Ltd. * * This file is part of NEVRAX NEL. * NEVRAX NEL is free software; you can redistribute it and/or modify @@ -27,41 +25,45 @@ #define NL_U_DECAL_H #include "nel/misc/types_nl.h" -#include "nel/3d/viewport.h" -#include "nel/3d/scissor.h" -#include "nel/3d/frustum.h" -#include "nel/misc/geom_ext.h" -#include "nel/misc/matrix.h" -#include "nel/misc/rgba.h" -#include "nel/misc/rect.h" -#include "nel/misc/bitmap.h" -#include "nel/misc/event_server.h" -#include "nel/misc/event_listener.h" -#include "nel/misc/hierarchical_timer.h" -#include "nel/3d/primitive_profile.h" +#include "nel/misc/uv.h" #include "nel/3d/u_transform.h" - namespace NL3D { +class CDecal; + class UDecal : public UTransform { -public: +public: /// Constructors UDecal() { _Object = NULL; } - UDecal(class CDecal *object) { _Object = (ITransformable*)object; }; + UDecal(class CDecal *object) { _Object = (ITransformable*)object; } /// Attach an object to this proxy void attach(class CDecal *object) { _Object = (ITransformable*)object; } /// Detach the object void detach() { _Object = NULL; } /// Return true if the proxy is empty() (not attached) - bool empty() const {return _Object==NULL;} + bool empty() const { return _Object == NULL; } /// For advanced usage, get the internal object ptr - class CDecal *getObjectPtr() const {return (CDecal*)_Object;} + class CDecal *getObjectPtr() const { return (CDecal*)_Object; } + + /// Set the decal texture from a filename + void setTexture(const std::string &filename); + + /// Set the material ID for batching (from CDecalManager::registerMaterial) + void setMaterialId(uint32 id); + + /// Set the UV sub-region within a texture atlas + void setUVCoord(const NLMISC::CUV &uv1, const NLMISC::CUV &uv2); + + /// Set the clipping mode (0=None, 1=Mask, 2=Geometry) + void setClippingMode(uint mode); + /// Mark this decal as static (only recomputed once) + void setStatic(bool isStatic); }; diff --git a/nel/src/3d/decal.cpp b/nel/src/3d/decal.cpp index 0d47b627d5..add338fffa 100644 --- a/nel/src/3d/decal.cpp +++ b/nel/src/3d/decal.cpp @@ -1,7 +1,7 @@ /** \file decal.cpp - * + * Projected texture decal system for NeL 3D. * - * $Id$ + * Based on the design from the intern report by Christopher Tarento (2007). */ /* Copyright, 2007 Nevrax Ltd. @@ -26,246 +26,256 @@ #include "std3d.h" #include "nel/3d/decal.h" -#include "nel/3d/shadow_map.h" #include "nel/3d/texture_file.h" #include "nel/3d/scene.h" #include "nel/3d/driver.h" -#include "nel/3d/scene_user.h" -#include "nel/3d/texture_user.h" - -#include "nel/3d/dru.h" - -#include +#include "nel/3d/clip_trav.h" +#include "nel/3d/visual_collision_manager.h" using namespace std; using namespace NL3D; using namespace NLMISC; - - +// *************************************************************************** +CDecalContext::CDecalContext() : ClipMode(DecalClipGeometry), DestTris(NULL) {} // *************************************************************************** -CDecalContext::CDecalContext() : Clipping(true), DestTris(NULL){} +CDecal::CDecal() : +_MaterialId(0), +_Touched(true), +_StableFrameCount(0), +_IsStatic(false), +_UV1(CUV(0, 0)), +_UV2(CUV(1, 1)) +{ +setOpacity(true); +setTransparency(false); +setIsRenderable(true); + +// Material setup: alpha-blended, double-sided, no z-write, with z-bias +_Mat.setShader(CMaterial::TShader::Normal); +_Mat.setBlend(true); +_Mat.setSrcBlend(CMaterial::srcalpha); +_Mat.setDstBlend(CMaterial::invsrcalpha); +_Mat.setZWrite(false); +_Mat.setDoubleSided(true); +_Mat.setZBias(-0.06f); + +// Texture environment: output = texture color/alpha +_Mat.texConstantColor(0, CRGBA::White); +_Mat.texEnvOpRGB(0, CMaterial::Replace); +_Mat.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor); +_Mat.texEnvOpAlpha(0, CMaterial::Replace); +_Mat.texEnvArg0Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha); + +// Default clipping mode: geometry clipping (best fillrate, see PDF 4.5.2) +_DecalContext.ClipMode = DecalClipGeometry; +} // *************************************************************************** -CDecal::CDecal() : - _VBInitialized(false), - _Touched(true), - _UV1(CUV(0,0)), - _UV2(CUV(1,1)) +void CDecal::initModel() { - //////////// - const float hg = 5.f; - - _Vertices.push_back( CVector( hg, hg, 0.f) ); - _Vertices.push_back( CVector(-hg, hg, 0.f) ); - _Vertices.push_back( CVector(-hg, -hg, 0.f) ); - _Vertices.push_back( CVector( hg, -hg, 0.f) ); - _Vertices.push_back( CVector( hg, 0.f, hg) ); - _Vertices.push_back( CVector(-hg, 0.f, hg) ); - _Vertices.push_back( CVector(-hg, 0.f, -hg) ); - _Vertices.push_back( CVector( hg, 0.f, -hg) ); - //////////// - - setOpacity(true); - setTransparency(false); - setIsRenderable(true); - setIsLightable(true); - - ///////////////////// - // vertex buffer - //////////////////// -/* _Vb.setPreferredMemory( CVertexBuffer::AGPPreferred, false ); - _Vb.setVertexFormat( CVertexBuffer::PositionFlag ); - _Vb.setNumVertices( 4 ); - - CVertexBufferReadWrite vbAccess; - _Vb.lock( vbAccess ); - vbAccess.setVertexCoord( 0, CVector( 0.f, 0.f, 0.f) ); - vbAccess.setVertexCoord( 1, CVector( 10.f, 0.f, 0.f) ); - vbAccess.setVertexCoord( 2, CVector( 10.f, 0.f, 10.f) ); - vbAccess.setVertexCoord( 3, CVector( 0.f, 0.f, 10.f) ); - -*/ - - /////////////////////// - // material - ////////////////////// - - ///set texture environment - _Mat.setShader(CMaterial::TShader::Normal);///fixed pipeline - - _Mat.texConstantColor(0, CRGBA::White); - - _Mat.texEnvOpRGB(0, CMaterial::Replace); - _Mat.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor);///out=tex - _Mat.texEnvArg1RGB(0, CMaterial::Constant, CMaterial::SrcColor); - - _Mat.texEnvOpAlpha(0, CMaterial::Replace); - _Mat.texEnvArg0Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha);///out=tex - _Mat.texEnvArg1Alpha(0, CMaterial::Constant, CMaterial::SrcAlpha); - - - _Mat.setBlend(true); - _Mat.setSrcBlend(CMaterial::srcalpha); - _Mat.setDstBlend(CMaterial::invsrcalpha); - _Mat.setZWrite(false); - _Mat.setDoubleSided(true); - - CTextureFile *tex = new CTextureFile("tex_decal.tga"); - tex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapLinear); - _Mat.enableUserTexMat(0, true); - _Mat.enableUserTexMat(1, true); - - - _Mat.setTexture(0, tex); - _Mat.setUserTexMat(0, CMatrix::Identity); - - _Mat.setTexCoordGen(0, true); - _Mat.setTexCoordGenMode(0, CMaterial::TexCoordGenObjectSpace); - - _Mat.setZBias(-0.06f); - - _Mat.getTexture(0)->setWrapS(ITexture::Clamp); - _Mat.getTexture(0)->setWrapT(ITexture::Clamp); +_LastCamPos = getOwnerScene()->getCam()->getMatrix().getPos(); +} +// *************************************************************************** +CDecal::~CDecal() +{ } + // *************************************************************************** -void CDecal::initModel() +void CDecal::registerBasic() { - _LastCamPos = getOwnerScene()->getCam()->getMatrix().getPos(); +CScene::registerModel(DecalId, TransformId, CDecal::creator); +} - _ShadowMap = new CShadowMap( &(getOwnerScene()->getRenderTrav().getShadowMapManager()) ); - +// *************************************************************************** +void CDecal::setTexture(const std::string &filename) +{ +CTextureFile *tex = new CTextureFile(filename); +tex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapLinear); +tex->setWrapS(ITexture::Clamp); +tex->setWrapT(ITexture::Clamp); +_Mat.setTexture(0, tex); } + // *************************************************************************** -CDecal::~CDecal() +// Clip method: bounding sphere vs frustum test (see PDF .6.1) +bool CDecal::clip() +{ +CScene *scene = getOwnerScene(); +CClipTrav &clipTrav = scene->getClipTrav(); + +// Compute bounding sphere from decal's world matrix +// The decal is a unit cube [0,1]^3, so center is at (0.5, 0.5, 0.5) in local space +CVector localCenter(0.5f, 0.5f, 0.5f); +CVector worldCenter = getWorldMatrix() * localCenter; + +// Radius: half-diagonal of the unit cube, scaled by the largest scale factor +// For a unit cube, half-diagonal = sqrt(3)/2 ~ 0.866 +float scaleI = (getWorldMatrix().getI()).norm(); +float scaleJ = (getWorldMatrix().getJ()).norm(); +float scaleK = (getWorldMatrix().getK()).norm(); +float maxScale = std::max(scaleI, std::max(scaleJ, scaleK)); +float radius = 0.866f * maxScale; + +// Test against camera frustum planes +const std::vector &pyramid = clipTrav.WorldPyramid; +for (uint i = 0; i < pyramid.size(); ++i) +{ +float d = pyramid[i] * worldCenter; +if (d > radius) { - delete _ShadowMap; +return false; } +} + +return true; +} + // *************************************************************************** -void CDecal::registerBasic() +// Render traversal: just register with the decal manager for batched rendering +void CDecal::traverseRender() { - CScene::registerModel( DecalId, TransformId, CDecal::creator); +getOwnerScene()->getRenderTrav().getDecalManager().addDecal(this, _MaterialId); } + // *************************************************************************** -void CDecal::traverseRender() +std::vector &CDecal::getVertices(const bool useVertexProgram) +{ +const NLMISC::CVector &camPos = getOwnerScene()->getCam()->getMatrix().getPos(); + +if (_IsStatic) +{ +// Static decal: only recompute on first touch. +// After that, use frame-count heuristic based on camera movement (4.5.4). +if (_Touched) { - // NL_ALLOC_CONTEXT( RdrDecal ) - // No rendering Op. But delay the clip and render after landscape rendering - +_LastCamPos = camPos; +computeDecal(useVertexProgram); +_Touched = false; +_StableFrameCount = 0; +} +} +else +{ +// Dynamic decal: recompute when camera moves significantly (4.6.1). +// Use visibility distance as threshold for recalculation. +if ((camPos - _LastCamPos).norm() >= 4.f) +{ +_Touched = true; +} - ///TODO christo pas beau le "Jean" - static std::string texName = "Jean"; - getOwnerScene()->getRenderTrav().getDecalManager().addDecal(this, texName); +if (_Touched) +{ +_LastCamPos = camPos; +computeDecal(useVertexProgram); +_Touched = false; +_StableFrameCount = 0; +} +else +{ +// Increment stable frame count. If a dynamic decal has been stable +// for many frames, it effectively becomes static (4.5.4 heuristic). +_StableFrameCount++; +} +} +return _Vertices; } + // *************************************************************************** -std::vector &CDecal::getVertices(const bool useVertexProgram) +// Face selection and clipping (see PDF .6.2 and 4.5.1/4.5.2) +void CDecal::computeDecal(const bool useVertexProgram) +{ +CScene *sc = getOwnerScene(); + +CVisualCollisionManager *vcm = sc->getVisualCollisionManagerForShadow(); +if (!vcm) +{ +nlwarning("CDecal::computeDecal: VisualCollisionManager not available"); +return; +} + +CDecalContext &context = _DecalContext; + +// Build clip planes from the unit cube corners transformed to world space +float decalSize = 1.0f; +_ClipCorners[0] = getWorldMatrix() * (CVector(0.f, 1.f, 0.f) * decalSize); +_ClipCorners[1] = getWorldMatrix() * (CVector(1.f, 1.f, 0.f) * decalSize); +_ClipCorners[2] = getWorldMatrix() * (CVector(1.f, 0.f, 0.f) * decalSize); +_ClipCorners[3] = getWorldMatrix() * (CVector(0.f, 0.f, 0.f) * decalSize); + +// Build 4 side clip planes from the corners (4.5.1) +context.WorldClipPlanes.resize(4); +context.WorldBBox.setMinMax( +getWorldMatrix() * (CVector(0.f, 0.f, 0.f) * decalSize), +getWorldMatrix() * (CVector(1.f, 1.f, 1.f) * decalSize)); + +for (uint i = 0; i < 4; ++i) { - ///position of decal has changed ?? - const NLMISC::CVector &camPos = getOwnerScene()->getCam()->getMatrix().getPos(); - if ( (camPos - _LastCamPos).norm() >= 4.f ) - { - _Touched = true; - } - - if( _Touched ) ///is NOT touched, direct render - { - _LastCamPos = camPos; - computeDecal(useVertexProgram); - } - - return _Vertices; +context.WorldClipPlanes[i].make( +_ClipCorners[i], +_ClipCorners[(i + 1) & 3], +_ClipCorners[i] + (_ClipCorners[(i + 1) & 3] - _ClipCorners[i]).norm() * CVector::K); +context.WorldClipPlanes[i].invert(); } +context.WorldMatrix = getWorldMatrix(); + +// Clear and collect triangles via visual collision +_Vertices.clear(); +context.DestTris = &_Vertices; +vcm->receiveDecal(context); + +// Generate UV coordinates from collected vertices +generateUVs(); +} + + // *************************************************************************** -void CDecal::computeDecal(const bool useVertexProgram) +// UV coordinate generation (see PDF 4.5.3) +// Uses inverse world matrix to project world-space vertices back to unit-cube local space. +// Camera position is subtracted for numerical stability (4.6.1). +void CDecal::generateUVs() +{ +_UVs.resize(_Vertices.size()); + +if (_Vertices.empty()) +return; + +// Compute worldToUV matrix: maps world position to [0,1] UV in local decal space +CMatrix invWorld = getWorldMatrix().inverted(); + +for (uint i = 0; i < _Vertices.size(); ++i) { - CScene *sc = getOwnerScene(); - - /// Verify that coll manager exists - CVisualCollisionManager *vcm; - if( !(vcm=sc->getVisualCollisionManagerForShadow()) ) - { - nlerror("VisualCollisionManager does NOT exist, NO decal rendering"); - return; - } - - - ///Preparing shadow map clipping planes - //CShadowMap sm( &(sc->getRenderTrav().getShadowMapManager()) ); - CDecalContext context; - - float decalSize = 1.0f; - _ClipCorners[0] = getWorldMatrix() * (CVector(0.f, 1.f, 0.f) * decalSize); - _ClipCorners[1] = getWorldMatrix() * (CVector(1.f, 1.f, 0.f) * decalSize); - _ClipCorners[2] = getWorldMatrix() * (CVector(1.f, 0.f, 0.f) * decalSize); - _ClipCorners[3] = getWorldMatrix() * (CVector(0.f, 0.f, 0.f) * decalSize); - - ///Unit Cube for clipping - context.WorldClipPlanes.resize(4); - context.WorldBBox.setMinMax( getWorldMatrix() * (CVector(0.f,0.f,0.f) * decalSize), getWorldMatrix() * (CVector(1.f,1.f,1.f) * decalSize) ); - - for(uint i=0; i<4; ++i) - { - context.WorldClipPlanes[i].make( - _ClipCorners[i], - _ClipCorners[(i + 1) & 3], - _ClipCorners[i] + (_ClipCorners[(i + 1) & 3] - _ClipCorners[i]).norm() * CVector::K);///&3 = %4 = %2^(3-1) - - context.WorldClipPlanes[i].invert(); - } - - - CMatrix refPosMatrix; - refPosMatrix.identity(); - refPosMatrix.setPos( sc->getCam()->getMatrix().getPos() ); - - CMatrix worldToUVMatrix = getReverseUVMatrix() * getWorldMatrix().inverted(); - //worldToUVMatrix = worldToUVMatrix * refPosMatrix; - - - //CMatrix world = getWorldMatrix(); - //smp.setWorldSpaceTextMat(CMatrix::Identity); - //_Mat.setUserTexMat(0, CMatrix::Identity); - - /*CShadowMapProjector smp; - vcm->receiveShadowMap(sc->getDriver(), &sm, CVector(0,0,0), _Mat, smp); - */ - - context.WorldMatrix = getWorldMatrix(); - - _Vertices.clear(); - context.DestTris = &_Vertices; - vcm->receiveDecal(context); - - - - sc->getDriver()->setupModelMatrix(getWorldMatrix()); - - CDRU::drawWiredBox( (CVector::K * (-10.f)), - CVector::I * decalSize, - CVector::J * decalSize, - (CVector::K * 20.f), - CRGBA::White, - *(sc->getDriver()) ); +// Transform world vertex to local decal space +CVector local = invWorld * _Vertices[i]; + +// Map local X,Y to UV, respecting the UV sub-region +float u = _UV1.U + local.x * (_UV2.U - _UV1.U); +float v = _UV1.V + (1.0f - local.y) * (_UV2.V - _UV1.V); +_UVs[i].U = u; +_UVs[i].V = v; } +} + // *************************************************************************** void CDecal::setUVCoord(const CUV uv1, const CUV uv2) { - _UV1 = uv1; - _UV2 = uv2; +_UV1 = uv1; +_UV2 = uv2; +_Touched = true; } diff --git a/nel/src/3d/decal_manager.cpp b/nel/src/3d/decal_manager.cpp index 00c933d44d..74767236c4 100644 --- a/nel/src/3d/decal_manager.cpp +++ b/nel/src/3d/decal_manager.cpp @@ -1,7 +1,7 @@ /** \file decal_manager.cpp - * + * Batched decal rendering manager for NeL 3D. * - * $Id$ + * Implements the rendering pseudocode from the intern report (§.6.3). */ /* Copyright, 2007 Nevrax Ltd. @@ -27,153 +27,184 @@ #include "nel/3d/decal_manager.h" #include "nel/3d/scene.h" -#include - - using namespace std; using namespace NLMISC; using namespace NL3D; // *************************************************************************** -CDecalManager::CDecalManager() : - _Touched(true), - _UsedVertices(0) +CDecalManager::CDecalManager() : + _NextMaterialId(0), + _UseVertexProgram(false) { - _VB.setPreferredMemory( CVertexBuffer::AGPVolatile, true ); - _VB.setVertexFormat( CVertexBuffer::PositionFlag ); + // Fixed-size AGP Volatile vertex buffer with Position + TexCoord0 (see PDF §4.5.4) + _VB.setPreferredMemory(CVertexBuffer::AGPVolatile, true); + _VB.setVertexFormat(CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag); + _VB.setNumVertices(NL3D_DECAL_VB_MAX_VERTICES); } + // *************************************************************************** CDecalManager::~CDecalManager() { - } + // *************************************************************************** void CDecalManager::clearAllDecals() { _Decals.clear(); - _UsedVertices = 0; - _Touched = true; } + // *************************************************************************** -void CDecalManager::addDecal(CDecal *decal, const string &texName) +void CDecalManager::addDecal(CDecal *decal, uint32 materialId) { - TDecalMap::iterator itEnd = _Decals.end(); - TDecalMap::iterator it = _Decals.find(texName); - if (it!=itEnd)///this key exist, add decal to vector - { - it->second.push_back(decal); - } - else///Not exist, create new vector - { - vector &vec = _Decals[texName]; - vec.push_back(decal); - } + _Decals[materialId].push_back(decal); +} - ///get number of vertices and compute prerender - //_UsedVertices += decal->getVertices(_UseVertexProgram).size(); - decal->getVertices(_UseVertexProgram); - _Touched = true; +// *************************************************************************** +uint32 CDecalManager::registerMaterial(const CMaterial &mat) +{ + CRegisteredMaterial rm; + rm.Mat = mat; + rm.Id = _NextMaterialId; + _Materials.push_back(rm); + return _NextMaterialId++; } + // *************************************************************************** -void CDecalManager::computeBatch() +// Rendering: implements the pseudocode from PDF §.6.3 +// +// for each material { +// count = 0; offset = 0 +// while there are remaining decals { +// length = number of vertices for current decal +// if count + length - offset > array buffer size { +// copy partial, render full buffer, count = 0 +// } else { +// copy remaining, increment decal +// } +// } +// if count > 0 { render remaining } +// } +void CDecalManager::flush(CScene *sc) { - //uint vertSize = _VB.getVertexSize(); - //_VB.setNumVertices( _UsedVertices );//tmp + if (_Decals.empty()) + return; - //CVertexBufferReadWrite vba; - //_VB.lock(vba); + IDriver *drv = sc->getRenderTrav().getDriver(); + drv->activeVertexProgram(NULL); + drv->activeVertexBuffer(_VB); + drv->setupModelMatrix(CMatrix::Identity); - //static const size = sizeof(CVector); + // Iterate over each material group + TDecalMap::iterator matIt = _Decals.begin(); + TDecalMap::iterator matEnd = _Decals.end(); - TDecalMap::iterator it = _Decals.begin(); - TDecalMap::iterator itEnd = _Decals.end(); - - //uint count=0; - vector accum; - for(;it!=itEnd;++it) + for (; matIt != matEnd; ++matIt) { - vector::iterator d = it->second.begin(); - vector::iterator dEnd = it->second.end(); - for(;d!=dEnd;++d) - { - vector &vec = (*d)->getVertices(_UseVertexProgram); - //static const uint length = vec.size(); - //if (length == 0) - // continue; - if(vec.size() == 0) - continue; + uint32 materialId = matIt->first; + std::vector &decals = matIt->second; + if (decals.empty()) + continue; - vector::iterator v = vec.begin(); - vector::iterator vEnd = vec.end(); - for(; v!=vEnd; ++v) + // Find the registered material for this ID + CMaterial *mat = NULL; + for (uint m = 0; m < _Materials.size(); ++m) + { + if (_Materials[m].Id == materialId) { - accum.push_back( *v ); + mat = &_Materials[m].Mat; + break; } - //memcpy( vba.getVertexCoordPointer(count), &vec[0], length*size ); - //count += length; } - } - - //_VB.setNumVertices( count ); - _VB.setNumVertices( (uint32)accum.size() ); - if (accum.size()) - { - CVertexBufferReadWrite vba; - _VB.lock(vba); - - memcpy(vba.getVertexCoordPointer(0), &accum[0], accum.size() * sizeof(CVector)); - // nlassert( count == _UsedVertices ); - nlassert(accum.size() % 3 == 0); - - vba.unlock(); - } - - _Touched = false; -} - -// *************************************************************************** -///main call for rendering -void CDecalManager::flush(CScene *sc) -{ - if (_Touched) - { - computeBatch(); - } - - IDriver *drv= sc->getRenderTrav().getDriver(); - drv->activeVertexProgram(NULL);///tmp + // If no registered material found, use the first decal's material + CMaterial fallbackMat; + if (!mat) + { + if (!decals.empty()) + { + fallbackMat = decals[0]->getMaterial(); + } + else + { + fallbackMat.initUnlit(); + fallbackMat.setDoubleSided(true); + } + mat = &fallbackMat; + } - drv->activeVertexBuffer(_VB); - - CMaterial mat;//tmp - mat.initUnlit(); - mat.setDoubleSided(true); - mat.setZBias(-50.0f); + uint32 count = 0; // current position in VB - vector jean = _Decals["Jean"];//tmp - + for (uint d = 0; d < decals.size(); ++d) + { + CDecal *decal = decals[d]; - drv->setupModelMatrix(CMatrix::Identity);///->OK - drv->renderRawTriangles( mat, 0, _VB.getNumVertices()/3 ); + // Get this decal's computed vertices and UVs + std::vector &verts = decal->getVertices(_UseVertexProgram); + const std::vector &uvs = decal->getUVs(); -} + if (verts.empty()) + continue; -// *************************************************************************** -void CDecalManager::renderStatic() -{ + uint32 length = (uint32)verts.size(); + uint32 offset = 0; -} - -// *************************************************************************** -void CDecalManager::renderDynamic() -{ + while (offset < length) + { + uint32 remaining = length - offset; + uint32 space = NL3D_DECAL_VB_MAX_VERTICES - count; + + if (remaining > space) + { + // Not enough space: fill what we can, render, reset + { + CVertexBufferReadWrite vba; + _VB.lock(vba); + for (uint32 i = 0; i < space; ++i) + { + *vba.getVertexCoordPointer(count + i) = verts[offset + i]; + if (offset + i < uvs.size()) + *vba.getTexCoordPointer(count + i, 0) = uvs[offset + i]; + } + } + offset += space; + count += space; + + // Render the full buffer + nlassert(count % 3 == 0); + drv->renderRawTriangles(*mat, 0, count / 3); + count = 0; + } + else + { + // Enough space: copy all remaining + { + CVertexBufferReadWrite vba; + _VB.lock(vba); + for (uint32 i = 0; i < remaining; ++i) + { + *vba.getVertexCoordPointer(count + i) = verts[offset + i]; + if (offset + i < uvs.size()) + *vba.getTexCoordPointer(count + i, 0) = uvs[offset + i]; + } + } + count += remaining; + offset = length; // done with this decal + } + } + } + // Render any remaining vertices for this material + if (count > 0) + { + nlassert(count % 3 == 0); + drv->renderRawTriangles(*mat, 0, count / 3); + } + } } diff --git a/nel/src/3d/u_decal.cpp b/nel/src/3d/u_decal.cpp new file mode 100644 index 0000000000..73867e504a --- /dev/null +++ b/nel/src/3d/u_decal.cpp @@ -0,0 +1,75 @@ +/** \file u_decal.cpp + * User interface implementation for projected texture decals. + */ + +/* Copyright, 2007 Nevrax Ltd. + * + * This file is part of NEVRAX NEL. + * NEVRAX NEL is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + + * NEVRAX NEL is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with NEVRAX NEL; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "std3d.h" + +#include "nel/3d/u_decal.h" +#include "nel/3d/decal.h" + +namespace NL3D +{ + +// *************************************************************************** +void UDecal::setTexture(const std::string &filename) +{ + CDecal *object = getObjectPtr(); + object->setTexture(filename); +} + +// *************************************************************************** +void UDecal::setMaterialId(uint32 id) +{ + CDecal *object = getObjectPtr(); + object->setMaterialId(id); +} + +// *************************************************************************** +void UDecal::setUVCoord(const NLMISC::CUV &uv1, const NLMISC::CUV &uv2) +{ + CDecal *object = getObjectPtr(); + object->setUVCoord(uv1, uv2); +} + +// *************************************************************************** +void UDecal::setClippingMode(uint mode) +{ + CDecal *object = getObjectPtr(); + TDecalClipMode clipMode; + switch (mode) + { + case 0: clipMode = DecalClipNone; break; + case 1: clipMode = DecalClipMask; break; + case 2: clipMode = DecalClipGeometry; break; + default: clipMode = DecalClipGeometry; break; + } + object->setClippingMode(clipMode); +} + +// *************************************************************************** +void UDecal::setStatic(bool isStatic) +{ + CDecal *object = getObjectPtr(); + object->setStatic(isStatic); +} + +} // NL3D diff --git a/nel/src/3d/visual_collision_mesh.cpp b/nel/src/3d/visual_collision_mesh.cpp index 9549d65054..c49750ba35 100644 --- a/nel/src/3d/visual_collision_mesh.cpp +++ b/nel/src/3d/visual_collision_mesh.cpp @@ -446,8 +446,9 @@ void CVisualCollisionMesh::receiveDecal(const NLMISC::CMatrix &instanceMatrix, // if triangle not clipped, add the triangle if( (triFlag & NL3D_VCM_SHADOW_NUM_CLIP_PLANE_MASK)==0 ) { - if(decalContext.Clipping) + if(decalContext.ClipMode == DecalClipGeometry) { + // Geometry clipping: clip vertices against bounding box planes (saves fillrate) static CPolygon clippedTri; clippedTri.Vertices.resize(3); clippedTri.Vertices[0] = instanceMatrix * _Vertices[triId[0]]; @@ -456,7 +457,7 @@ void CVisualCollisionMesh::receiveDecal(const NLMISC::CMatrix &instanceMatrix, clippedTri.clip(decalContext.WorldClipPlanes); if (clippedTri.Vertices.size() >= 3) - { + { for(uint k=0; kpush_back( clippedTri.Vertices[0] ); @@ -464,15 +465,14 @@ void CVisualCollisionMesh::receiveDecal(const NLMISC::CMatrix &instanceMatrix, decalContext.DestTris->push_back( clippedTri.Vertices[k + 2] ); } } - } else { + // No clipping or Mask clipping: use faces as-is (mask handles edges via texture stage) decalContext.DestTris->push_back( instanceMatrix * _Vertices[triId[0]] ); decalContext.DestTris->push_back( instanceMatrix * _Vertices[triId[1]] ); decalContext.DestTris->push_back( instanceMatrix * _Vertices[triId[2]] ); } - } } From 3aa6720ea58b0bfcd3dd83fb7b2c832830b115ea Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 20:27:26 +0000 Subject: [PATCH 08/24] Revert accidentally modified unrelated files (line ending changes) Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- .../lua_ide_dll_nevrax/source/libapi/libapi.c | 294 ++-- .../source/standard/lauxlib.c | 1202 ++++++------- .../source/standard/lbaselib.c | 1368 +++++++-------- .../source/standard/ldblib.c | 618 +++---- .../source/standard/liolib.c | 1520 ++++++++-------- .../source/standard/lmathlib.c | 512 +++--- .../source/standard/loadlib.c | 410 ++--- .../source/standard/lstrlib.c | 1560 ++++++++--------- .../source/standard/ltablib.c | 520 +++--- .../src/ai_service/doc/doxycfg/doxygen.css | 124 +- .../sadge_lib/include/text_input.h | 2 +- .../sadge_lib/include/text_output.h | 2 +- 12 files changed, 4066 insertions(+), 4066 deletions(-) diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/libapi/libapi.c b/ryzom/client/src/lua_ide_dll_nevrax/source/libapi/libapi.c index 5f2a007d46..ddc5c620e3 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/libapi/libapi.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/libapi/libapi.c @@ -1,147 +1,147 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#include "stdafx.h" - -#include "../ide2/DebuggerMessages.h" - -static HWND g_hWnd; - -using namespace std; - -// maximum mumber of lines the output console should have -static const WORD MAX_CONSOLE_LINES = 500; - -BOOL isWindowsNT(void) -{ - static BOOL once = FALSE; - static BOOL isNT = FALSE; - - if (!once) - { - OSVERSIONINFO osver; - osver.dwOSVersionInfoSize = sizeof(osver); - if (GetVersionEx(&osver)) - if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) - isNT = TRUE; - once = TRUE; - } - return isNT; -} - -static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd) -{ - char tmp[20], *tty; - if (isWindowsNT()) - tty = "ConsoleWindowClass"; - else - tty = "tty"; - if (GetClassName(wnd, tmp, sizeof(tmp)) && !strcmp(tmp, tty)) - { - DWORD wndproc, thisproc = GetCurrentProcessId(); - GetWindowThreadProcessId(wnd, &wndproc); - if (wndproc == thisproc) { - *((HWND*)retwnd) = wnd; - return FALSE; - } - } - return TRUE; -} - - -static BOOL CtrlHandler(DWORD fdwCtrlType) -{ - switch (fdwCtrlType) - { - // Handle the CTRL+C signal. - - case CTRL_C_EVENT: - case CTRL_BREAK_EVENT: - return TRUE; - - // CTRL+CLOSE: confirm that the user wants to exit. - - case CTRL_CLOSE_EVENT: - - case CTRL_LOGOFF_EVENT: - - case CTRL_SHUTDOWN_EVENT: - - default: - - return FALSE; - } -} - -void AttachConsole() -{ - int hConHandle; - long lStdHandle; - CONSOLE_SCREEN_BUFFER_INFO coninfo; - FILE *fp; - HWND hWndConsole; - HMENU hSysMenu; - -// allocate a console for this app - FreeConsole(); - AllocConsole(); - -//disable system menu -> close box - EnumWindows(EnumttyWindow, (long)(&hWndConsole)); - hSysMenu = GetSystemMenu(hWndConsole, FALSE); - ModifyMenu(hSysMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED, 0, 0); - ShowWindow(hWndConsole, SW_SHOW); - -// set the screen buffer to be big enough to let us scroll text - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); - coninfo.dwSize.Y = MAX_CONSOLE_LINES; - SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); - -// redirect unbuffered STDOUT to the console - lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE); - hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); - fp = _fdopen( hConHandle, "w" ); - *stdout = *fp; - setvbuf( stdout, NULL, _IONBF, 0 ); - -// redirect unbuffered STDIN to the console - lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE); - hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); - ::SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_MOUSE_INPUT); - fp = _fdopen( hConHandle, "r" ); - *stdin = *fp; - setvbuf( stdin, NULL, _IONBF, 0 ); - -// redirect unbuffered STDERR to the console - lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE); - hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); - fp = _fdopen( hConHandle, "w" ); - *stderr = *fp; - setvbuf( stderr, NULL, _IONBF, 0 ); - -// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog -// point to console as well - ios::sync_with_stdio(); - - SetConsoleCtrlHandler( - (PHANDLER_ROUTINE) CtrlHandler, // handler function - TRUE); // add to list -} - -BOOL InitLibAPI(HWND hWnd) -{ - g_hWnd = hWnd; -} +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdafx.h" + +#include "../ide2/DebuggerMessages.h" + +static HWND g_hWnd; + +using namespace std; + +// maximum mumber of lines the output console should have +static const WORD MAX_CONSOLE_LINES = 500; + +BOOL isWindowsNT(void) +{ + static BOOL once = FALSE; + static BOOL isNT = FALSE; + + if (!once) + { + OSVERSIONINFO osver; + osver.dwOSVersionInfoSize = sizeof(osver); + if (GetVersionEx(&osver)) + if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) + isNT = TRUE; + once = TRUE; + } + return isNT; +} + +static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd) +{ + char tmp[20], *tty; + if (isWindowsNT()) + tty = "ConsoleWindowClass"; + else + tty = "tty"; + if (GetClassName(wnd, tmp, sizeof(tmp)) && !strcmp(tmp, tty)) + { + DWORD wndproc, thisproc = GetCurrentProcessId(); + GetWindowThreadProcessId(wnd, &wndproc); + if (wndproc == thisproc) { + *((HWND*)retwnd) = wnd; + return FALSE; + } + } + return TRUE; +} + + +static BOOL CtrlHandler(DWORD fdwCtrlType) +{ + switch (fdwCtrlType) + { + // Handle the CTRL+C signal. + + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + return TRUE; + + // CTRL+CLOSE: confirm that the user wants to exit. + + case CTRL_CLOSE_EVENT: + + case CTRL_LOGOFF_EVENT: + + case CTRL_SHUTDOWN_EVENT: + + default: + + return FALSE; + } +} + +void AttachConsole() +{ + int hConHandle; + long lStdHandle; + CONSOLE_SCREEN_BUFFER_INFO coninfo; + FILE *fp; + HWND hWndConsole; + HMENU hSysMenu; + +// allocate a console for this app + FreeConsole(); + AllocConsole(); + +//disable system menu -> close box + EnumWindows(EnumttyWindow, (long)(&hWndConsole)); + hSysMenu = GetSystemMenu(hWndConsole, FALSE); + ModifyMenu(hSysMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED, 0, 0); + ShowWindow(hWndConsole, SW_SHOW); + +// set the screen buffer to be big enough to let us scroll text + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); + coninfo.dwSize.Y = MAX_CONSOLE_LINES; + SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); + +// redirect unbuffered STDOUT to the console + lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE); + hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + fp = _fdopen( hConHandle, "w" ); + *stdout = *fp; + setvbuf( stdout, NULL, _IONBF, 0 ); + +// redirect unbuffered STDIN to the console + lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE); + hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + ::SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_MOUSE_INPUT); + fp = _fdopen( hConHandle, "r" ); + *stdin = *fp; + setvbuf( stdin, NULL, _IONBF, 0 ); + +// redirect unbuffered STDERR to the console + lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE); + hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + fp = _fdopen( hConHandle, "w" ); + *stderr = *fp; + setvbuf( stderr, NULL, _IONBF, 0 ); + +// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog +// point to console as well + ios::sync_with_stdio(); + + SetConsoleCtrlHandler( + (PHANDLER_ROUTINE) CtrlHandler, // handler function + TRUE); // add to list +} + +BOOL InitLibAPI(HWND hWnd) +{ + g_hWnd = hWnd; +} diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lauxlib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lauxlib.c index 1ef9877354..adc01cb898 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lauxlib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lauxlib.c @@ -1,601 +1,601 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include -#include -#include -#include -#include - - -/* This file uses only the official API of Lua. -** Any function declared here could be written as an application function. -*/ - -#define lauxlib_c - -#include "lua.h" - -#include "lauxlib.h" - - -/* number of prereserved references (for internal use) */ -#define RESERVED_REFS 2 - -/* reserved references */ -#define FREELIST_REF 1 /* free list of references */ -#define ARRAYSIZE_REF 2 /* array sizes */ - - -/* convert a stack index to positive */ -#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \ - lua_gettop(L) + (i) + 1) - - -/* -** {====================================================== -** Error-report functions -** ======================================================= -*/ - - -LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) { - lua_Debug ar; - lua_getstack(L, 0, &ar); - lua_getinfo(L, "n", &ar); - if (strcmp(ar.namewhat, "method") == 0) { - narg--; /* do not count `self' */ - if (narg == 0) /* error is in the self argument itself? */ - return luaL_error(L, "calling `%s' on bad self (%s)", ar.name, extramsg); - } - if (ar.name == NULL) - ar.name = "?"; - return luaL_error(L, "bad argument #%d to `%s' (%s)", - narg, ar.name, extramsg); -} - - -LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) { - const char *msg = lua_pushfstring(L, "%s expected, got %s", - tname, lua_typename(L, lua_type(L,narg))); - return luaL_argerror(L, narg, msg); -} - - -static void tag_error (lua_State *L, int narg, int tag) { - luaL_typerror(L, narg, lua_typename(L, tag)); -} - - -LUALIB_API void luaL_where (lua_State *L, int level) { - lua_Debug ar; - if (lua_getstack(L, level, &ar)) { /* check function at level */ - lua_getinfo(L, "Snl", &ar); /* get info about it */ - if (ar.currentline > 0) { /* is there info? */ - lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); - return; - } - } - lua_pushliteral(L, ""); /* else, no information available... */ -} - - -LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { - va_list argp; - va_start(argp, fmt); - luaL_where(L, 1); - lua_pushvfstring(L, fmt, argp); - va_end(argp); - lua_concat(L, 2); - return lua_error(L); -} - -/* }====================================================== */ - - -LUALIB_API int luaL_findstring (const char *name, const char *const list[]) { - int i; - for (i=0; list[i]; i++) - if (strcmp(list[i], name) == 0) - return i; - return -1; /* name not found */ -} - - -LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { - lua_pushstring(L, tname); - lua_rawget(L, LUA_REGISTRYINDEX); /* get registry.name */ - if (!lua_isnil(L, -1)) /* name already in use? */ - return 0; /* leave previous value on top, but return 0 */ - lua_pop(L, 1); - lua_newtable(L); /* create metatable */ - lua_pushstring(L, tname); - lua_pushvalue(L, -2); - lua_rawset(L, LUA_REGISTRYINDEX); /* registry.name = metatable */ - lua_pushvalue(L, -1); - lua_pushstring(L, tname); - lua_rawset(L, LUA_REGISTRYINDEX); /* registry[metatable] = name */ - return 1; -} - - -LUALIB_API void luaL_getmetatable (lua_State *L, const char *tname) { - lua_pushstring(L, tname); - lua_rawget(L, LUA_REGISTRYINDEX); -} - - -LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { - const char *tn; - if (!lua_getmetatable(L, ud)) return NULL; /* no metatable? */ - lua_rawget(L, LUA_REGISTRYINDEX); /* get registry[metatable] */ - tn = lua_tostring(L, -1); - if (tn && (strcmp(tn, tname) == 0)) { - lua_pop(L, 1); - return lua_touserdata(L, ud); - } - else { - lua_pop(L, 1); - return NULL; - } -} - - -LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) { - if (!lua_checkstack(L, space)) - luaL_error(L, "stack overflow (%s)", mes); -} - - -LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) { - if (lua_type(L, narg) != t) - tag_error(L, narg, t); -} - - -LUALIB_API void luaL_checkany (lua_State *L, int narg) { - if (lua_type(L, narg) == LUA_TNONE) - luaL_argerror(L, narg, "value expected"); -} - - -LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) { - const char *s = lua_tostring(L, narg); - if (!s) tag_error(L, narg, LUA_TSTRING); - if (len) *len = lua_strlen(L, narg); - return s; -} - - -LUALIB_API const char *luaL_optlstring (lua_State *L, int narg, - const char *def, size_t *len) { - if (lua_isnoneornil(L, narg)) { - if (len) - *len = (def ? strlen(def) : 0); - return def; - } - else return luaL_checklstring(L, narg, len); -} - - -LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) { - lua_Number d = lua_tonumber(L, narg); - if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ - tag_error(L, narg, LUA_TNUMBER); - return d; -} - - -LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) { - if (lua_isnoneornil(L, narg)) return def; - else return luaL_checknumber(L, narg); -} - - -LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { - if (!lua_getmetatable(L, obj)) /* no metatable? */ - return 0; - lua_pushstring(L, event); - lua_rawget(L, -2); - if (lua_isnil(L, -1)) { - lua_pop(L, 2); /* remove metatable and metafield */ - return 0; - } - else { - lua_remove(L, -2); /* remove only metatable */ - return 1; - } -} - - -LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { - obj = abs_index(L, obj); - if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ - return 0; - lua_pushvalue(L, obj); - lua_call(L, 1, 1); - return 1; -} - - -LUALIB_API void luaL_openlib (lua_State *L, const char *libname, - const luaL_reg *l, int nup) { - if (libname) { - lua_pushstring(L, libname); - lua_gettable(L, LUA_GLOBALSINDEX); /* check whether lib already exists */ - if (lua_isnil(L, -1)) { /* no? */ - lua_pop(L, 1); - lua_newtable(L); /* create it */ - lua_pushstring(L, libname); - lua_pushvalue(L, -2); - lua_settable(L, LUA_GLOBALSINDEX); /* register it with given name */ - } - lua_insert(L, -(nup+1)); /* move library table to below upvalues */ - } - for (; l->name; l++) { - int i; - lua_pushstring(L, l->name); - for (i=0; ifunc, nup); - lua_settable(L, -(nup+3)); - } - lua_pop(L, nup); /* remove upvalues */ -} - - - -/* -** {====================================================== -** getn-setn: size for arrays -** ======================================================= -*/ - -static int checkint (lua_State *L, int topop) { - int n = (int)lua_tonumber(L, -1); - if (n == 0 && !lua_isnumber(L, -1)) n = -1; - lua_pop(L, topop); - return n; -} - - -static void getsizes (lua_State *L) { - lua_rawgeti(L, LUA_REGISTRYINDEX, ARRAYSIZE_REF); - if (lua_isnil(L, -1)) { /* no `size' table? */ - lua_pop(L, 1); /* remove nil */ - lua_newtable(L); /* create it */ - lua_pushvalue(L, -1); /* `size' will be its own metatable */ - lua_setmetatable(L, -2); - lua_pushliteral(L, "__mode"); - lua_pushliteral(L, "k"); - lua_rawset(L, -3); /* metatable(N).__mode = "k" */ - lua_pushvalue(L, -1); - lua_rawseti(L, LUA_REGISTRYINDEX, ARRAYSIZE_REF); /* store in register */ - } -} - - -void luaL_setn (lua_State *L, int t, int n) { - t = abs_index(L, t); - lua_pushliteral(L, "n"); - lua_rawget(L, t); - if (checkint(L, 1) >= 0) { /* is there a numeric field `n'? */ - lua_pushliteral(L, "n"); /* use it */ - lua_pushnumber(L, (lua_Number)n); - lua_rawset(L, t); - } - else { /* use `sizes' */ - getsizes(L); - lua_pushvalue(L, t); - lua_pushnumber(L, (lua_Number)n); - lua_rawset(L, -3); /* sizes[t] = n */ - lua_pop(L, 1); /* remove `sizes' */ - } -} - - -int luaL_getn (lua_State *L, int t) { - int n; - t = abs_index(L, t); - lua_pushliteral(L, "n"); /* try t.n */ - lua_rawget(L, t); - if ((n = checkint(L, 1)) >= 0) return n; - getsizes(L); /* else try sizes[t] */ - lua_pushvalue(L, t); - lua_rawget(L, -2); - if ((n = checkint(L, 2)) >= 0) return n; - for (n = 1; ; n++) { /* else must count elements */ - lua_rawgeti(L, t, n); - if (lua_isnil(L, -1)) break; - lua_pop(L, 1); - } - lua_pop(L, 1); - return n - 1; -} - -/* }====================================================== */ - - - -/* -** {====================================================== -** Generic Buffer manipulation -** ======================================================= -*/ - - -#define bufflen(B) ((B)->p - (B)->buffer) -#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B))) - -#define LIMIT (LUA_MINSTACK/2) - - -static int emptybuffer (luaL_Buffer *B) { - size_t l = bufflen(B); - if (l == 0) return 0; /* put nothing on stack */ - else { - lua_pushlstring(B->L, B->buffer, l); - B->p = B->buffer; - B->lvl++; - return 1; - } -} - - -static void adjuststack (luaL_Buffer *B) { - if (B->lvl > 1) { - lua_State *L = B->L; - int toget = 1; /* number of levels to concat */ - size_t toplen = lua_strlen(L, -1); - do { - size_t l = lua_strlen(L, -(toget+1)); - if (B->lvl - toget + 1 >= LIMIT || toplen > l) { - toplen += l; - toget++; - } - else break; - } while (toget < B->lvl); - lua_concat(L, toget); - B->lvl = B->lvl - toget + 1; - } -} - - -LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) { - if (emptybuffer(B)) - adjuststack(B); - return B->buffer; -} - - -LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { - while (l--) - luaL_putchar(B, *s++); -} - - -LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { - luaL_addlstring(B, s, strlen(s)); -} - - -LUALIB_API void luaL_pushresult (luaL_Buffer *B) { - emptybuffer(B); - lua_concat(B->L, B->lvl); - B->lvl = 1; -} - - -LUALIB_API void luaL_addvalue (luaL_Buffer *B) { - lua_State *L = B->L; - size_t vl = lua_strlen(L, -1); - if (vl <= bufffree(B)) { /* fit into buffer? */ - memcpy(B->p, lua_tostring(L, -1), vl); /* put it there */ - B->p += vl; - lua_pop(L, 1); /* remove from stack */ - } - else { - if (emptybuffer(B)) - lua_insert(L, -2); /* put buffer before new value */ - B->lvl++; /* add new value into B stack */ - adjuststack(B); - } -} - - -LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { - B->L = L; - B->p = B->buffer; - B->lvl = 0; -} - -/* }====================================================== */ - - -LUALIB_API int luaL_ref (lua_State *L, int t) { - int ref; - t = abs_index(L, t); - if (lua_isnil(L, -1)) { - lua_pop(L, 1); /* remove from stack */ - return LUA_REFNIL; /* `nil' has a unique fixed reference */ - } - lua_rawgeti(L, t, FREELIST_REF); /* get first free element */ - ref = (int)lua_tonumber(L, -1); /* ref = t[FREELIST_REF] */ - lua_pop(L, 1); /* remove it from stack */ - if (ref != 0) { /* any free element? */ - lua_rawgeti(L, t, ref); /* remove it from list */ - lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */ - } - else { /* no free elements */ - ref = luaL_getn(L, t); - if (ref < RESERVED_REFS) - ref = RESERVED_REFS; /* skip reserved references */ - ref++; /* create new reference */ - luaL_setn(L, t, ref); - } - lua_rawseti(L, t, ref); - return ref; -} - - -LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { - if (ref >= 0) { - t = abs_index(L, t); - lua_rawgeti(L, t, FREELIST_REF); - lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */ - lua_pushnumber(L, (lua_Number)ref); - lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */ - } -} - - - -/* -** {====================================================== -** Load functions -** ======================================================= -*/ - -typedef struct LoadF { - FILE *f; - char buff[LUAL_BUFFERSIZE]; -} LoadF; - - -static const char *getF (lua_State *L, void *ud, size_t *size) { - LoadF *lf = (LoadF *)ud; - (void)L; - if (feof(lf->f)) return NULL; - *size = fread(lf->buff, 1, LUAL_BUFFERSIZE, lf->f); - return (*size > 0) ? lf->buff : NULL; -} - - -static int errfile (lua_State *L, int fnameindex) { - const char *filename = lua_tostring(L, fnameindex) + 1; - lua_pushfstring(L, "cannot read %s: %s", filename, strerror(errno)); - lua_remove(L, fnameindex); - return LUA_ERRFILE; -} - - -LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) { - LoadF lf; - int status, readstatus; - int c; - int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ - if (filename == NULL) { - lua_pushliteral(L, "=stdin"); - lf.f = stdin; - } - else { - lua_pushfstring(L, "@%s", filename); - lf.f = fopen(filename, "r"); - } - if (lf.f == NULL) return errfile(L, fnameindex); /* unable to open file */ - c = ungetc(getc(lf.f), lf.f); - if (!(isspace(c) || isprint(c)) && lf.f != stdin) { /* binary file? */ - fclose(lf.f); - lf.f = fopen(filename, "rb"); /* reopen in binary mode */ - if (lf.f == NULL) return errfile(L, fnameindex); /* unable to reopen file */ - } - status = lua_load(L, getF, &lf, lua_tostring(L, -1)); - readstatus = ferror(lf.f); - if (lf.f != stdin) fclose(lf.f); /* close file (even in case of errors) */ - if (readstatus) { - lua_settop(L, fnameindex); /* ignore results from `lua_load' */ - return errfile(L, fnameindex); - } - lua_remove(L, fnameindex); - return status; -} - - -typedef struct LoadS { - const char *s; - size_t size; -} LoadS; - - -static const char *getS (lua_State *L, void *ud, size_t *size) { - LoadS *ls = (LoadS *)ud; - (void)L; - if (ls->size == 0) return NULL; - *size = ls->size; - ls->size = 0; - return ls->s; -} - - -LUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size, - const char *name) { - LoadS ls; - ls.s = buff; - ls.size = size; - return lua_load(L, getS, &ls, name); -} - -/* }====================================================== */ - - -/* -** {====================================================== -** compatibility code -** ======================================================= -*/ - - -static void callalert (lua_State *L, int status) { - if (status != 0) { - lua_getglobal(L, "_ALERT"); - if (lua_isfunction(L, -1)) { - lua_insert(L, -2); - lua_call(L, 1, 0); - } - else { /* no _ALERT function; print it on stderr */ - fprintf(stderr, "%s\n", lua_tostring(L, -2)); - lua_pop(L, 2); /* remove error message and _ALERT */ - } - } -} - - -static int aux_do (lua_State *L, int status) { - if (status == 0) { /* parse OK? */ - status = lua_pcall(L, 0, LUA_MULTRET, 0); /* call main */ - } - callalert(L, status); - return status; -} - - -LUALIB_API int lua_dofile (lua_State *L, const char *filename) { - return aux_do(L, luaL_loadfile(L, filename)); -} - - -LUALIB_API int lua_dobuffer (lua_State *L, const char *buff, size_t size, - const char *name) { - return aux_do(L, luaL_loadbuffer(L, buff, size, name)); -} - - -LUALIB_API int lua_dostring (lua_State *L, const char *str) { - return lua_dobuffer(L, str, strlen(str), str); -} - -/* }====================================================== */ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include +#include +#include +#include +#include + + +/* This file uses only the official API of Lua. +** Any function declared here could be written as an application function. +*/ + +#define lauxlib_c + +#include "lua.h" + +#include "lauxlib.h" + + +/* number of prereserved references (for internal use) */ +#define RESERVED_REFS 2 + +/* reserved references */ +#define FREELIST_REF 1 /* free list of references */ +#define ARRAYSIZE_REF 2 /* array sizes */ + + +/* convert a stack index to positive */ +#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \ + lua_gettop(L) + (i) + 1) + + +/* +** {====================================================== +** Error-report functions +** ======================================================= +*/ + + +LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) { + lua_Debug ar; + lua_getstack(L, 0, &ar); + lua_getinfo(L, "n", &ar); + if (strcmp(ar.namewhat, "method") == 0) { + narg--; /* do not count `self' */ + if (narg == 0) /* error is in the self argument itself? */ + return luaL_error(L, "calling `%s' on bad self (%s)", ar.name, extramsg); + } + if (ar.name == NULL) + ar.name = "?"; + return luaL_error(L, "bad argument #%d to `%s' (%s)", + narg, ar.name, extramsg); +} + + +LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) { + const char *msg = lua_pushfstring(L, "%s expected, got %s", + tname, lua_typename(L, lua_type(L,narg))); + return luaL_argerror(L, narg, msg); +} + + +static void tag_error (lua_State *L, int narg, int tag) { + luaL_typerror(L, narg, lua_typename(L, tag)); +} + + +LUALIB_API void luaL_where (lua_State *L, int level) { + lua_Debug ar; + if (lua_getstack(L, level, &ar)) { /* check function at level */ + lua_getinfo(L, "Snl", &ar); /* get info about it */ + if (ar.currentline > 0) { /* is there info? */ + lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); + return; + } + } + lua_pushliteral(L, ""); /* else, no information available... */ +} + + +LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + luaL_where(L, 1); + lua_pushvfstring(L, fmt, argp); + va_end(argp); + lua_concat(L, 2); + return lua_error(L); +} + +/* }====================================================== */ + + +LUALIB_API int luaL_findstring (const char *name, const char *const list[]) { + int i; + for (i=0; list[i]; i++) + if (strcmp(list[i], name) == 0) + return i; + return -1; /* name not found */ +} + + +LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { + lua_pushstring(L, tname); + lua_rawget(L, LUA_REGISTRYINDEX); /* get registry.name */ + if (!lua_isnil(L, -1)) /* name already in use? */ + return 0; /* leave previous value on top, but return 0 */ + lua_pop(L, 1); + lua_newtable(L); /* create metatable */ + lua_pushstring(L, tname); + lua_pushvalue(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); /* registry.name = metatable */ + lua_pushvalue(L, -1); + lua_pushstring(L, tname); + lua_rawset(L, LUA_REGISTRYINDEX); /* registry[metatable] = name */ + return 1; +} + + +LUALIB_API void luaL_getmetatable (lua_State *L, const char *tname) { + lua_pushstring(L, tname); + lua_rawget(L, LUA_REGISTRYINDEX); +} + + +LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { + const char *tn; + if (!lua_getmetatable(L, ud)) return NULL; /* no metatable? */ + lua_rawget(L, LUA_REGISTRYINDEX); /* get registry[metatable] */ + tn = lua_tostring(L, -1); + if (tn && (strcmp(tn, tname) == 0)) { + lua_pop(L, 1); + return lua_touserdata(L, ud); + } + else { + lua_pop(L, 1); + return NULL; + } +} + + +LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) { + if (!lua_checkstack(L, space)) + luaL_error(L, "stack overflow (%s)", mes); +} + + +LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) { + if (lua_type(L, narg) != t) + tag_error(L, narg, t); +} + + +LUALIB_API void luaL_checkany (lua_State *L, int narg) { + if (lua_type(L, narg) == LUA_TNONE) + luaL_argerror(L, narg, "value expected"); +} + + +LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) { + const char *s = lua_tostring(L, narg); + if (!s) tag_error(L, narg, LUA_TSTRING); + if (len) *len = lua_strlen(L, narg); + return s; +} + + +LUALIB_API const char *luaL_optlstring (lua_State *L, int narg, + const char *def, size_t *len) { + if (lua_isnoneornil(L, narg)) { + if (len) + *len = (def ? strlen(def) : 0); + return def; + } + else return luaL_checklstring(L, narg, len); +} + + +LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) { + lua_Number d = lua_tonumber(L, narg); + if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ + tag_error(L, narg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) { + if (lua_isnoneornil(L, narg)) return def; + else return luaL_checknumber(L, narg); +} + + +LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { + if (!lua_getmetatable(L, obj)) /* no metatable? */ + return 0; + lua_pushstring(L, event); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); /* remove metatable and metafield */ + return 0; + } + else { + lua_remove(L, -2); /* remove only metatable */ + return 1; + } +} + + +LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { + obj = abs_index(L, obj); + if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ + return 0; + lua_pushvalue(L, obj); + lua_call(L, 1, 1); + return 1; +} + + +LUALIB_API void luaL_openlib (lua_State *L, const char *libname, + const luaL_reg *l, int nup) { + if (libname) { + lua_pushstring(L, libname); + lua_gettable(L, LUA_GLOBALSINDEX); /* check whether lib already exists */ + if (lua_isnil(L, -1)) { /* no? */ + lua_pop(L, 1); + lua_newtable(L); /* create it */ + lua_pushstring(L, libname); + lua_pushvalue(L, -2); + lua_settable(L, LUA_GLOBALSINDEX); /* register it with given name */ + } + lua_insert(L, -(nup+1)); /* move library table to below upvalues */ + } + for (; l->name; l++) { + int i; + lua_pushstring(L, l->name); + for (i=0; ifunc, nup); + lua_settable(L, -(nup+3)); + } + lua_pop(L, nup); /* remove upvalues */ +} + + + +/* +** {====================================================== +** getn-setn: size for arrays +** ======================================================= +*/ + +static int checkint (lua_State *L, int topop) { + int n = (int)lua_tonumber(L, -1); + if (n == 0 && !lua_isnumber(L, -1)) n = -1; + lua_pop(L, topop); + return n; +} + + +static void getsizes (lua_State *L) { + lua_rawgeti(L, LUA_REGISTRYINDEX, ARRAYSIZE_REF); + if (lua_isnil(L, -1)) { /* no `size' table? */ + lua_pop(L, 1); /* remove nil */ + lua_newtable(L); /* create it */ + lua_pushvalue(L, -1); /* `size' will be its own metatable */ + lua_setmetatable(L, -2); + lua_pushliteral(L, "__mode"); + lua_pushliteral(L, "k"); + lua_rawset(L, -3); /* metatable(N).__mode = "k" */ + lua_pushvalue(L, -1); + lua_rawseti(L, LUA_REGISTRYINDEX, ARRAYSIZE_REF); /* store in register */ + } +} + + +void luaL_setn (lua_State *L, int t, int n) { + t = abs_index(L, t); + lua_pushliteral(L, "n"); + lua_rawget(L, t); + if (checkint(L, 1) >= 0) { /* is there a numeric field `n'? */ + lua_pushliteral(L, "n"); /* use it */ + lua_pushnumber(L, (lua_Number)n); + lua_rawset(L, t); + } + else { /* use `sizes' */ + getsizes(L); + lua_pushvalue(L, t); + lua_pushnumber(L, (lua_Number)n); + lua_rawset(L, -3); /* sizes[t] = n */ + lua_pop(L, 1); /* remove `sizes' */ + } +} + + +int luaL_getn (lua_State *L, int t) { + int n; + t = abs_index(L, t); + lua_pushliteral(L, "n"); /* try t.n */ + lua_rawget(L, t); + if ((n = checkint(L, 1)) >= 0) return n; + getsizes(L); /* else try sizes[t] */ + lua_pushvalue(L, t); + lua_rawget(L, -2); + if ((n = checkint(L, 2)) >= 0) return n; + for (n = 1; ; n++) { /* else must count elements */ + lua_rawgeti(L, t, n); + if (lua_isnil(L, -1)) break; + lua_pop(L, 1); + } + lua_pop(L, 1); + return n - 1; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + + +#define bufflen(B) ((B)->p - (B)->buffer) +#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B))) + +#define LIMIT (LUA_MINSTACK/2) + + +static int emptybuffer (luaL_Buffer *B) { + size_t l = bufflen(B); + if (l == 0) return 0; /* put nothing on stack */ + else { + lua_pushlstring(B->L, B->buffer, l); + B->p = B->buffer; + B->lvl++; + return 1; + } +} + + +static void adjuststack (luaL_Buffer *B) { + if (B->lvl > 1) { + lua_State *L = B->L; + int toget = 1; /* number of levels to concat */ + size_t toplen = lua_strlen(L, -1); + do { + size_t l = lua_strlen(L, -(toget+1)); + if (B->lvl - toget + 1 >= LIMIT || toplen > l) { + toplen += l; + toget++; + } + else break; + } while (toget < B->lvl); + lua_concat(L, toget); + B->lvl = B->lvl - toget + 1; + } +} + + +LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) { + if (emptybuffer(B)) + adjuststack(B); + return B->buffer; +} + + +LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { + while (l--) + luaL_putchar(B, *s++); +} + + +LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { + luaL_addlstring(B, s, strlen(s)); +} + + +LUALIB_API void luaL_pushresult (luaL_Buffer *B) { + emptybuffer(B); + lua_concat(B->L, B->lvl); + B->lvl = 1; +} + + +LUALIB_API void luaL_addvalue (luaL_Buffer *B) { + lua_State *L = B->L; + size_t vl = lua_strlen(L, -1); + if (vl <= bufffree(B)) { /* fit into buffer? */ + memcpy(B->p, lua_tostring(L, -1), vl); /* put it there */ + B->p += vl; + lua_pop(L, 1); /* remove from stack */ + } + else { + if (emptybuffer(B)) + lua_insert(L, -2); /* put buffer before new value */ + B->lvl++; /* add new value into B stack */ + adjuststack(B); + } +} + + +LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { + B->L = L; + B->p = B->buffer; + B->lvl = 0; +} + +/* }====================================================== */ + + +LUALIB_API int luaL_ref (lua_State *L, int t) { + int ref; + t = abs_index(L, t); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* remove from stack */ + return LUA_REFNIL; /* `nil' has a unique fixed reference */ + } + lua_rawgeti(L, t, FREELIST_REF); /* get first free element */ + ref = (int)lua_tonumber(L, -1); /* ref = t[FREELIST_REF] */ + lua_pop(L, 1); /* remove it from stack */ + if (ref != 0) { /* any free element? */ + lua_rawgeti(L, t, ref); /* remove it from list */ + lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */ + } + else { /* no free elements */ + ref = luaL_getn(L, t); + if (ref < RESERVED_REFS) + ref = RESERVED_REFS; /* skip reserved references */ + ref++; /* create new reference */ + luaL_setn(L, t, ref); + } + lua_rawseti(L, t, ref); + return ref; +} + + +LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { + if (ref >= 0) { + t = abs_index(L, t); + lua_rawgeti(L, t, FREELIST_REF); + lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */ + lua_pushnumber(L, (lua_Number)ref); + lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */ + } +} + + + +/* +** {====================================================== +** Load functions +** ======================================================= +*/ + +typedef struct LoadF { + FILE *f; + char buff[LUAL_BUFFERSIZE]; +} LoadF; + + +static const char *getF (lua_State *L, void *ud, size_t *size) { + LoadF *lf = (LoadF *)ud; + (void)L; + if (feof(lf->f)) return NULL; + *size = fread(lf->buff, 1, LUAL_BUFFERSIZE, lf->f); + return (*size > 0) ? lf->buff : NULL; +} + + +static int errfile (lua_State *L, int fnameindex) { + const char *filename = lua_tostring(L, fnameindex) + 1; + lua_pushfstring(L, "cannot read %s: %s", filename, strerror(errno)); + lua_remove(L, fnameindex); + return LUA_ERRFILE; +} + + +LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) { + LoadF lf; + int status, readstatus; + int c; + int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ + if (filename == NULL) { + lua_pushliteral(L, "=stdin"); + lf.f = stdin; + } + else { + lua_pushfstring(L, "@%s", filename); + lf.f = fopen(filename, "r"); + } + if (lf.f == NULL) return errfile(L, fnameindex); /* unable to open file */ + c = ungetc(getc(lf.f), lf.f); + if (!(isspace(c) || isprint(c)) && lf.f != stdin) { /* binary file? */ + fclose(lf.f); + lf.f = fopen(filename, "rb"); /* reopen in binary mode */ + if (lf.f == NULL) return errfile(L, fnameindex); /* unable to reopen file */ + } + status = lua_load(L, getF, &lf, lua_tostring(L, -1)); + readstatus = ferror(lf.f); + if (lf.f != stdin) fclose(lf.f); /* close file (even in case of errors) */ + if (readstatus) { + lua_settop(L, fnameindex); /* ignore results from `lua_load' */ + return errfile(L, fnameindex); + } + lua_remove(L, fnameindex); + return status; +} + + +typedef struct LoadS { + const char *s; + size_t size; +} LoadS; + + +static const char *getS (lua_State *L, void *ud, size_t *size) { + LoadS *ls = (LoadS *)ud; + (void)L; + if (ls->size == 0) return NULL; + *size = ls->size; + ls->size = 0; + return ls->s; +} + + +LUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size, + const char *name) { + LoadS ls; + ls.s = buff; + ls.size = size; + return lua_load(L, getS, &ls, name); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** compatibility code +** ======================================================= +*/ + + +static void callalert (lua_State *L, int status) { + if (status != 0) { + lua_getglobal(L, "_ALERT"); + if (lua_isfunction(L, -1)) { + lua_insert(L, -2); + lua_call(L, 1, 0); + } + else { /* no _ALERT function; print it on stderr */ + fprintf(stderr, "%s\n", lua_tostring(L, -2)); + lua_pop(L, 2); /* remove error message and _ALERT */ + } + } +} + + +static int aux_do (lua_State *L, int status) { + if (status == 0) { /* parse OK? */ + status = lua_pcall(L, 0, LUA_MULTRET, 0); /* call main */ + } + callalert(L, status); + return status; +} + + +LUALIB_API int lua_dofile (lua_State *L, const char *filename) { + return aux_do(L, luaL_loadfile(L, filename)); +} + + +LUALIB_API int lua_dobuffer (lua_State *L, const char *buff, size_t size, + const char *name) { + return aux_do(L, luaL_loadbuffer(L, buff, size, name)); +} + + +LUALIB_API int lua_dostring (lua_State *L, const char *str) { + return lua_dobuffer(L, str, strlen(str), str); +} + +/* }====================================================== */ diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lbaselib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lbaselib.c index 9f613bcdb9..1b320924d2 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lbaselib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lbaselib.c @@ -1,684 +1,684 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - - -#include -#include -#include -#include - -#define lbaselib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - - - -/* -** If your system does not support `stdout', you can just remove this function. -** If you need, you can define your own `print' function, following this -** model but changing `fputs' to put the strings at a proper place -** (a console window or a log file, for instance). -*/ -static int luaB_print (lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - int i; - lua_getglobal(L, "tostring"); - for (i=1; i<=n; i++) { - const char *s; - lua_pushvalue(L, -1); /* function to be called */ - lua_pushvalue(L, i); /* value to print */ - lua_call(L, 1, 1); - s = lua_tostring(L, -1); /* get result */ - if (s == NULL) - return luaL_error(L, "`tostring' must return a string to `print'"); - if (i>1) fputs("\t", stdout); - fputs(s, stdout); - lua_pop(L, 1); /* pop result */ - } - fputs("\n", stdout); - return 0; -} - - -static int luaB_tonumber (lua_State *L) { - int base = luaL_optint(L, 2, 10); - if (base == 10) { /* standard conversion */ - luaL_checkany(L, 1); - if (lua_isnumber(L, 1)) { - lua_pushnumber(L, lua_tonumber(L, 1)); - return 1; - } - } - else { - const char *s1 = luaL_checkstring(L, 1); - char *s2; - unsigned long n; - luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); - n = strtoul(s1, &s2, base); - if (s1 != s2) { /* at least one valid digit? */ - while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */ - if (*s2 == '\0') { /* no invalid trailing characters? */ - lua_pushnumber(L, (lua_Number)n); - return 1; - } - } - } - lua_pushnil(L); /* else not a number */ - return 1; -} - - -static int luaB_error (lua_State *L) { - int level = luaL_optint(L, 2, 1); - luaL_checkany(L, 1); - if (!lua_isstring(L, 1) || level == 0) - lua_pushvalue(L, 1); /* propagate error message without changes */ - else { /* add extra information */ - luaL_where(L, level); - lua_pushvalue(L, 1); - lua_concat(L, 2); - } - return lua_error(L); -} - - -static int luaB_getmetatable (lua_State *L) { - luaL_checkany(L, 1); - if (!lua_getmetatable(L, 1)) { - lua_pushnil(L); - return 1; /* no metatable */ - } - luaL_getmetafield(L, 1, "__metatable"); - return 1; /* returns either __metatable field (if present) or metatable */ -} - - -static int luaB_setmetatable (lua_State *L) { - int t = lua_type(L, 2); - luaL_checktype(L, 1, LUA_TTABLE); - luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, - "nil or table expected"); - if (luaL_getmetafield(L, 1, "__metatable")) - luaL_error(L, "cannot change a protected metatable"); - lua_settop(L, 2); - lua_setmetatable(L, 1); - return 1; -} - - -static void getfunc (lua_State *L) { - if (lua_isfunction(L, 1)) lua_pushvalue(L, 1); - else { - lua_Debug ar; - int level = luaL_optint(L, 1, 1); - luaL_argcheck(L, level >= 0, 1, "level must be non-negative"); - if (lua_getstack(L, level, &ar) == 0) - luaL_argerror(L, 1, "invalid level"); - lua_getinfo(L, "f", &ar); - if (lua_isnil(L, -1)) - luaL_error(L, "no function environment for tail call at level %d", - level); - } -} - - -static int aux_getfenv (lua_State *L) { - lua_getfenv(L, -1); - lua_pushliteral(L, "__fenv"); - lua_rawget(L, -2); - return !lua_isnil(L, -1); -} - - -static int luaB_getfenv (lua_State *L) { - getfunc(L); - if (!aux_getfenv(L)) /* __fenv not defined? */ - lua_pop(L, 1); /* remove it, to return real environment */ - return 1; -} - - -static int luaB_setfenv (lua_State *L) { - luaL_checktype(L, 2, LUA_TTABLE); - getfunc(L); - if (aux_getfenv(L)) /* __fenv defined? */ - luaL_error(L, "`setfenv' cannot change a protected environment"); - else - lua_pop(L, 2); /* remove __fenv and real environment table */ - lua_pushvalue(L, 2); - if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) - lua_replace(L, LUA_GLOBALSINDEX); - else if (lua_setfenv(L, -2) == 0) - luaL_error(L, "`setfenv' cannot change environment of given function"); - return 0; -} - - -static int luaB_rawequal (lua_State *L) { - luaL_checkany(L, 1); - luaL_checkany(L, 2); - lua_pushboolean(L, lua_rawequal(L, 1, 2)); - return 1; -} - - -static int luaB_rawget (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - luaL_checkany(L, 2); - lua_rawget(L, 1); - return 1; -} - -static int luaB_rawset (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - luaL_checkany(L, 2); - luaL_checkany(L, 3); - lua_rawset(L, 1); - return 1; -} - - -static int luaB_gcinfo (lua_State *L) { - lua_pushnumber(L, (lua_Number)lua_getgccount(L)); - lua_pushnumber(L, (lua_Number)lua_getgcthreshold(L)); - return 2; -} - - -static int luaB_collectgarbage (lua_State *L) { - lua_setgcthreshold(L, luaL_optint(L, 1, 0)); - return 0; -} - - -static int luaB_type (lua_State *L) { - luaL_checkany(L, 1); - lua_pushstring(L, lua_typename(L, lua_type(L, 1))); - return 1; -} - - -static int luaB_next (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - lua_settop(L, 2); /* create a 2nd argument if there isn't one */ - if (lua_next(L, 1)) - return 2; - else { - lua_pushnil(L); - return 1; - } -} - - -static int luaB_pairs (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - lua_pushliteral(L, "next"); - lua_rawget(L, LUA_GLOBALSINDEX); /* return generator, */ - lua_pushvalue(L, 1); /* state, */ - lua_pushnil(L); /* and initial value */ - return 3; -} - - -static int luaB_ipairs (lua_State *L) { - lua_Number i = lua_tonumber(L, 2); - luaL_checktype(L, 1, LUA_TTABLE); - if (i == 0 && lua_isnone(L, 2)) { /* `for' start? */ - lua_pushliteral(L, "ipairs"); - lua_rawget(L, LUA_GLOBALSINDEX); /* return generator, */ - lua_pushvalue(L, 1); /* state, */ - lua_pushnumber(L, 0); /* and initial value */ - return 3; - } - else { /* `for' step */ - i++; /* next value */ - lua_pushnumber(L, i); - lua_rawgeti(L, 1, (int)i); - return (lua_isnil(L, -1)) ? 0 : 2; - } -} - - -static int load_aux (lua_State *L, int status) { - if (status == 0) /* OK? */ - return 1; - else { - lua_pushnil(L); - lua_insert(L, -2); /* put before error message */ - return 2; /* return nil plus error message */ - } -} - - -static int luaB_loadstring (lua_State *L) { - size_t l; - const char *s = luaL_checklstring(L, 1, &l); - const char *chunkname = luaL_optstring(L, 2, s); - return load_aux(L, luaL_loadbuffer(L, s, l, chunkname)); -} - - -static int luaB_loadfile (lua_State *L) { - const char *fname = luaL_optstring(L, 1, NULL); - return load_aux(L, luaL_loadfile(L, fname)); -} - - -static int luaB_dofile (lua_State *L) { - const char *fname = luaL_optstring(L, 1, NULL); - int status = luaL_loadfile(L, fname); - if (status != 0) lua_error(L); - lua_call(L, 0, LUA_MULTRET); - return lua_gettop(L) - 1; -} - - -static int luaB_assert (lua_State *L) { - luaL_checkany(L, 1); - if (!lua_toboolean(L, 1)) - return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!")); - lua_settop(L, 1); - return 1; -} - - -static int luaB_unpack (lua_State *L) { - int n, i; - luaL_checktype(L, 1, LUA_TTABLE); - n = luaL_getn(L, 1); - luaL_checkstack(L, n, "table too big to unpack"); - for (i=1; i<=n; i++) /* push arg[1...n] */ - lua_rawgeti(L, 1, i); - return n; -} - - -static int luaB_pcall (lua_State *L) { - int status; - luaL_checkany(L, 1); - status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); - lua_pushboolean(L, (status == 0)); - lua_insert(L, 1); - return lua_gettop(L); /* return status + all results */ -} - - -static int luaB_xpcall (lua_State *L) { - int status; - luaL_checkany(L, 2); - lua_settop(L, 2); - lua_insert(L, 1); /* put error function under function to be called */ - status = lua_pcall(L, 0, LUA_MULTRET, 1); - lua_pushboolean(L, (status == 0)); - lua_replace(L, 1); - return lua_gettop(L); /* return status + all results */ -} - - -static int luaB_tostring (lua_State *L) { - char buff[64]; - luaL_checkany(L, 1); - if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */ - return 1; /* use its value */ - switch (lua_type(L, 1)) { - case LUA_TNUMBER: - lua_pushstring(L, lua_tostring(L, 1)); - return 1; - case LUA_TSTRING: - lua_pushvalue(L, 1); - return 1; - case LUA_TBOOLEAN: - lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false")); - return 1; - case LUA_TTABLE: - sprintf(buff, "table: %p", lua_topointer(L, 1)); - break; - case LUA_TFUNCTION: - sprintf(buff, "function: %p", lua_topointer(L, 1)); - break; - case LUA_TUSERDATA: - case LUA_TLIGHTUSERDATA: - sprintf(buff, "userdata: %p", lua_touserdata(L, 1)); - break; - case LUA_TTHREAD: - sprintf(buff, "thread: %p", (void *)lua_tothread(L, 1)); - break; - case LUA_TNIL: - lua_pushliteral(L, "nil"); - return 1; - } - lua_pushstring(L, buff); - return 1; -} - - -static int luaB_newproxy (lua_State *L) { - lua_settop(L, 1); - lua_newuserdata(L, 0); /* create proxy */ - if (lua_toboolean(L, 1) == 0) - return 1; /* no metatable */ - else if (lua_isboolean(L, 1)) { - lua_newtable(L); /* create a new metatable `m' ... */ - lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */ - lua_pushboolean(L, 1); - lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */ - } - else { - int validproxy = 0; /* to check if weaktable[metatable(u)] == true */ - if (lua_getmetatable(L, 1)) { - lua_rawget(L, lua_upvalueindex(1)); - validproxy = lua_toboolean(L, -1); - lua_pop(L, 1); /* remove value */ - } - luaL_argcheck(L, validproxy, 1, "boolean or proxy expected"); - lua_getmetatable(L, 1); /* metatable is valid; get it */ - } - lua_setmetatable(L, 2); - return 1; -} - - -/* -** {====================================================== -** `require' function -** ======================================================= -*/ - - -/* name of global that holds table with loaded packages */ -#define REQTAB "_LOADED" - -/* name of global that holds the search path for packages */ -#define LUA_PATH "LUA_PATH" - -#ifndef LUA_PATH_SEP -#define LUA_PATH_SEP ';' -#endif - -#ifndef LUA_PATH_MARK -#define LUA_PATH_MARK '?' -#endif - -#ifndef LUA_PATH_DEFAULT -#define LUA_PATH_DEFAULT "?;?.lua" -#endif - - -static const char *getpath (lua_State *L) { - const char *path; - lua_getglobal(L, LUA_PATH); /* try global variable */ - path = lua_tostring(L, -1); - lua_pop(L, 1); - if (path) return path; - path = getenv(LUA_PATH); /* else try environment variable */ - if (path) return path; - return LUA_PATH_DEFAULT; /* else use default */ -} - - -static const char *pushnextpath (lua_State *L, const char *path) { - const char *l; - if (*path == '\0') return NULL; /* no more paths */ - if (*path == LUA_PATH_SEP) path++; /* skip separator */ - l = strchr(path, LUA_PATH_SEP); /* find next separator */ - if (l == NULL) l = path+strlen(path); - lua_pushlstring(L, path, l - path); /* directory name */ - return l; -} - - -static void pushcomposename (lua_State *L) { - const char *path = lua_tostring(L, -1); - const char *wild; - int n = 1; - while ((wild = strchr(path, LUA_PATH_MARK)) != NULL) { - /* is there stack space for prefix, name, and eventual last sufix? */ - luaL_checkstack(L, 3, "too many marks in a path component"); - lua_pushlstring(L, path, wild - path); /* push prefix */ - lua_pushvalue(L, 1); /* push package name (in place of MARK) */ - path = wild + 1; /* continue after MARK */ - n += 2; - } - lua_pushstring(L, path); /* push last sufix (`n' already includes this) */ - lua_concat(L, n); -} - - -static int luaB_require (lua_State *L) { - const char *path; - int status = LUA_ERRFILE; /* not found (yet) */ - luaL_checkstring(L, 1); - lua_settop(L, 1); - lua_getglobal(L, REQTAB); - if (!lua_istable(L, 2)) return luaL_error(L, "`" REQTAB "' is not a table"); - path = getpath(L); - lua_pushvalue(L, 1); /* check package's name in book-keeping table */ - lua_rawget(L, 2); - if (lua_toboolean(L, -1)) /* is it there? */ - return 1; /* package is already loaded; return its result */ - else { /* must load it */ - while (status == LUA_ERRFILE) { - lua_settop(L, 3); /* reset stack position */ - if ((path = pushnextpath(L, path)) == NULL) break; - pushcomposename(L); - status = luaL_loadfile(L, lua_tostring(L, -1)); /* try to load it */ - } - } - switch (status) { - case 0: { - lua_getglobal(L, "_REQUIREDNAME"); /* save previous name */ - lua_insert(L, -2); /* put it below function */ - lua_pushvalue(L, 1); - lua_setglobal(L, "_REQUIREDNAME"); /* set new name */ - lua_call(L, 0, 1); /* run loaded module */ - lua_insert(L, -2); /* put result below previous name */ - lua_setglobal(L, "_REQUIREDNAME"); /* reset to previous name */ - if (lua_isnil(L, -1)) { /* no/nil return? */ - lua_pushboolean(L, 1); - lua_replace(L, -2); /* replace to true */ - } - lua_pushvalue(L, 1); - lua_pushvalue(L, -2); - lua_rawset(L, 2); /* mark it as loaded */ - return 1; /* return value */ - } - case LUA_ERRFILE: { /* file not found */ - return luaL_error(L, "could not load package `%s' from path `%s'", - lua_tostring(L, 1), getpath(L)); - } - default: { - return luaL_error(L, "error loading package `%s' (%s)", - lua_tostring(L, 1), lua_tostring(L, -1)); - } - } -} - -/* }====================================================== */ - - -static const luaL_reg base_funcs[] = { - {"error", luaB_error}, - {"getmetatable", luaB_getmetatable}, - {"setmetatable", luaB_setmetatable}, - {"getfenv", luaB_getfenv}, - {"setfenv", luaB_setfenv}, - {"next", luaB_next}, - {"ipairs", luaB_ipairs}, - {"pairs", luaB_pairs}, - {"print", luaB_print}, - {"tonumber", luaB_tonumber}, - {"tostring", luaB_tostring}, - {"type", luaB_type}, - {"assert", luaB_assert}, - {"unpack", luaB_unpack}, - {"rawequal", luaB_rawequal}, - {"rawget", luaB_rawget}, - {"rawset", luaB_rawset}, - {"pcall", luaB_pcall}, - {"xpcall", luaB_xpcall}, - {"collectgarbage", luaB_collectgarbage}, - {"gcinfo", luaB_gcinfo}, - {"loadfile", luaB_loadfile}, - {"dofile", luaB_dofile}, - {"loadstring", luaB_loadstring}, - {"require", luaB_require}, - {NULL, NULL} -}; - - -/* -** {====================================================== -** Coroutine library -** ======================================================= -*/ - -static int auxresume (lua_State *L, lua_State *co, int narg) { - int status; - if (!lua_checkstack(co, narg)) - luaL_error(L, "too many arguments to resume"); - lua_xmove(L, co, narg); - status = lua_resume(co, narg); - if (status == 0) { - int nres = lua_gettop(co); - if (!lua_checkstack(L, nres)) - luaL_error(L, "too many results to resume"); - lua_xmove(co, L, nres); /* move yielded values */ - return nres; - } - else { - lua_xmove(co, L, 1); /* move error message */ - return -1; /* error flag */ - } -} - - -static int luaB_coresume (lua_State *L) { - lua_State *co = lua_tothread(L, 1); - int r; - luaL_argcheck(L, co, 1, "coroutine expected"); - r = auxresume(L, co, lua_gettop(L) - 1); - if (r < 0) { - lua_pushboolean(L, 0); - lua_insert(L, -2); - return 2; /* return false + error message */ - } - else { - lua_pushboolean(L, 1); - lua_insert(L, -(r + 1)); - return r + 1; /* return true + `resume' returns */ - } -} - - -static int luaB_auxwrap (lua_State *L) { - lua_State *co = lua_tothread(L, lua_upvalueindex(1)); - int r = auxresume(L, co, lua_gettop(L)); - if (r < 0) { - if (lua_isstring(L, -1)) { /* error object is a string? */ - luaL_where(L, 1); /* add extra info */ - lua_insert(L, -2); - lua_concat(L, 2); - } - lua_error(L); /* propagate error */ - } - return r; -} - - -static int luaB_cocreate (lua_State *L) { - lua_State *NL = lua_newthread(L); - luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, - "Lua function expected"); - lua_pushvalue(L, 1); /* move function to top */ - lua_xmove(L, NL, 1); /* move function from L to NL */ - return 1; -} - - -static int luaB_cowrap (lua_State *L) { - luaB_cocreate(L); - lua_pushcclosure(L, luaB_auxwrap, 1); - return 1; -} - - -static int luaB_yield (lua_State *L) { - return lua_yield(L, lua_gettop(L)); -} - - -static int luaB_costatus (lua_State *L) { - lua_State *co = lua_tothread(L, 1); - luaL_argcheck(L, co, 1, "coroutine expected"); - if (L == co) lua_pushliteral(L, "running"); - else { - lua_Debug ar; - if (lua_getstack(co, 0, &ar) == 0 && lua_gettop(co) == 0) - lua_pushliteral(L, "dead"); - else - lua_pushliteral(L, "suspended"); - } - return 1; -} - - -static const luaL_reg co_funcs[] = { - {"create", luaB_cocreate}, - {"wrap", luaB_cowrap}, - {"resume", luaB_coresume}, - {"yield", luaB_yield}, - {"status", luaB_costatus}, - {NULL, NULL} -}; - -/* }====================================================== */ - - - -static void base_open (lua_State *L) { - lua_pushliteral(L, "_G"); - lua_pushvalue(L, LUA_GLOBALSINDEX); - luaL_openlib(L, NULL, base_funcs, 0); /* open lib into global table */ - lua_pushliteral(L, "_VERSION"); - lua_pushliteral(L, LUA_VERSION); - lua_rawset(L, -3); /* set global _VERSION */ - /* `newproxy' needs a weaktable as upvalue */ - lua_pushliteral(L, "newproxy"); - lua_newtable(L); /* new table `w' */ - lua_pushvalue(L, -1); /* `w' will be its own metatable */ - lua_setmetatable(L, -2); - lua_pushliteral(L, "__mode"); - lua_pushliteral(L, "k"); - lua_rawset(L, -3); /* metatable(w).__mode = "k" */ - lua_pushcclosure(L, luaB_newproxy, 1); - lua_rawset(L, -3); /* set global `newproxy' */ - lua_rawset(L, -1); /* set global _G */ -} - - -LUALIB_API int luaopen_base (lua_State *L) { - base_open(L); - luaL_openlib(L, LUA_COLIBNAME, co_funcs, 0); - lua_newtable(L); - lua_setglobal(L, REQTAB); - return 0; -} - +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + + +#include +#include +#include +#include + +#define lbaselib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + + +/* +** If your system does not support `stdout', you can just remove this function. +** If you need, you can define your own `print' function, following this +** model but changing `fputs' to put the strings at a proper place +** (a console window or a log file, for instance). +*/ +static int luaB_print (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + lua_getglobal(L, "tostring"); + for (i=1; i<=n; i++) { + const char *s; + lua_pushvalue(L, -1); /* function to be called */ + lua_pushvalue(L, i); /* value to print */ + lua_call(L, 1, 1); + s = lua_tostring(L, -1); /* get result */ + if (s == NULL) + return luaL_error(L, "`tostring' must return a string to `print'"); + if (i>1) fputs("\t", stdout); + fputs(s, stdout); + lua_pop(L, 1); /* pop result */ + } + fputs("\n", stdout); + return 0; +} + + +static int luaB_tonumber (lua_State *L) { + int base = luaL_optint(L, 2, 10); + if (base == 10) { /* standard conversion */ + luaL_checkany(L, 1); + if (lua_isnumber(L, 1)) { + lua_pushnumber(L, lua_tonumber(L, 1)); + return 1; + } + } + else { + const char *s1 = luaL_checkstring(L, 1); + char *s2; + unsigned long n; + luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); + n = strtoul(s1, &s2, base); + if (s1 != s2) { /* at least one valid digit? */ + while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */ + if (*s2 == '\0') { /* no invalid trailing characters? */ + lua_pushnumber(L, (lua_Number)n); + return 1; + } + } + } + lua_pushnil(L); /* else not a number */ + return 1; +} + + +static int luaB_error (lua_State *L) { + int level = luaL_optint(L, 2, 1); + luaL_checkany(L, 1); + if (!lua_isstring(L, 1) || level == 0) + lua_pushvalue(L, 1); /* propagate error message without changes */ + else { /* add extra information */ + luaL_where(L, level); + lua_pushvalue(L, 1); + lua_concat(L, 2); + } + return lua_error(L); +} + + +static int luaB_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); + return 1; /* no metatable */ + } + luaL_getmetafield(L, 1, "__metatable"); + return 1; /* returns either __metatable field (if present) or metatable */ +} + + +static int luaB_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, + "nil or table expected"); + if (luaL_getmetafield(L, 1, "__metatable")) + luaL_error(L, "cannot change a protected metatable"); + lua_settop(L, 2); + lua_setmetatable(L, 1); + return 1; +} + + +static void getfunc (lua_State *L) { + if (lua_isfunction(L, 1)) lua_pushvalue(L, 1); + else { + lua_Debug ar; + int level = luaL_optint(L, 1, 1); + luaL_argcheck(L, level >= 0, 1, "level must be non-negative"); + if (lua_getstack(L, level, &ar) == 0) + luaL_argerror(L, 1, "invalid level"); + lua_getinfo(L, "f", &ar); + if (lua_isnil(L, -1)) + luaL_error(L, "no function environment for tail call at level %d", + level); + } +} + + +static int aux_getfenv (lua_State *L) { + lua_getfenv(L, -1); + lua_pushliteral(L, "__fenv"); + lua_rawget(L, -2); + return !lua_isnil(L, -1); +} + + +static int luaB_getfenv (lua_State *L) { + getfunc(L); + if (!aux_getfenv(L)) /* __fenv not defined? */ + lua_pop(L, 1); /* remove it, to return real environment */ + return 1; +} + + +static int luaB_setfenv (lua_State *L) { + luaL_checktype(L, 2, LUA_TTABLE); + getfunc(L); + if (aux_getfenv(L)) /* __fenv defined? */ + luaL_error(L, "`setfenv' cannot change a protected environment"); + else + lua_pop(L, 2); /* remove __fenv and real environment table */ + lua_pushvalue(L, 2); + if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) + lua_replace(L, LUA_GLOBALSINDEX); + else if (lua_setfenv(L, -2) == 0) + luaL_error(L, "`setfenv' cannot change environment of given function"); + return 0; +} + + +static int luaB_rawequal (lua_State *L) { + luaL_checkany(L, 1); + luaL_checkany(L, 2); + lua_pushboolean(L, lua_rawequal(L, 1, 2)); + return 1; +} + + +static int luaB_rawget (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + lua_rawget(L, 1); + return 1; +} + +static int luaB_rawset (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + luaL_checkany(L, 3); + lua_rawset(L, 1); + return 1; +} + + +static int luaB_gcinfo (lua_State *L) { + lua_pushnumber(L, (lua_Number)lua_getgccount(L)); + lua_pushnumber(L, (lua_Number)lua_getgcthreshold(L)); + return 2; +} + + +static int luaB_collectgarbage (lua_State *L) { + lua_setgcthreshold(L, luaL_optint(L, 1, 0)); + return 0; +} + + +static int luaB_type (lua_State *L) { + luaL_checkany(L, 1); + lua_pushstring(L, lua_typename(L, lua_type(L, 1))); + return 1; +} + + +static int luaB_next (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 2); /* create a 2nd argument if there isn't one */ + if (lua_next(L, 1)) + return 2; + else { + lua_pushnil(L); + return 1; + } +} + + +static int luaB_pairs (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushliteral(L, "next"); + lua_rawget(L, LUA_GLOBALSINDEX); /* return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushnil(L); /* and initial value */ + return 3; +} + + +static int luaB_ipairs (lua_State *L) { + lua_Number i = lua_tonumber(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + if (i == 0 && lua_isnone(L, 2)) { /* `for' start? */ + lua_pushliteral(L, "ipairs"); + lua_rawget(L, LUA_GLOBALSINDEX); /* return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushnumber(L, 0); /* and initial value */ + return 3; + } + else { /* `for' step */ + i++; /* next value */ + lua_pushnumber(L, i); + lua_rawgeti(L, 1, (int)i); + return (lua_isnil(L, -1)) ? 0 : 2; + } +} + + +static int load_aux (lua_State *L, int status) { + if (status == 0) /* OK? */ + return 1; + else { + lua_pushnil(L); + lua_insert(L, -2); /* put before error message */ + return 2; /* return nil plus error message */ + } +} + + +static int luaB_loadstring (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + const char *chunkname = luaL_optstring(L, 2, s); + return load_aux(L, luaL_loadbuffer(L, s, l, chunkname)); +} + + +static int luaB_loadfile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + return load_aux(L, luaL_loadfile(L, fname)); +} + + +static int luaB_dofile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + int status = luaL_loadfile(L, fname); + if (status != 0) lua_error(L); + lua_call(L, 0, LUA_MULTRET); + return lua_gettop(L) - 1; +} + + +static int luaB_assert (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_toboolean(L, 1)) + return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!")); + lua_settop(L, 1); + return 1; +} + + +static int luaB_unpack (lua_State *L) { + int n, i; + luaL_checktype(L, 1, LUA_TTABLE); + n = luaL_getn(L, 1); + luaL_checkstack(L, n, "table too big to unpack"); + for (i=1; i<=n; i++) /* push arg[1...n] */ + lua_rawgeti(L, 1, i); + return n; +} + + +static int luaB_pcall (lua_State *L) { + int status; + luaL_checkany(L, 1); + status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); + lua_pushboolean(L, (status == 0)); + lua_insert(L, 1); + return lua_gettop(L); /* return status + all results */ +} + + +static int luaB_xpcall (lua_State *L) { + int status; + luaL_checkany(L, 2); + lua_settop(L, 2); + lua_insert(L, 1); /* put error function under function to be called */ + status = lua_pcall(L, 0, LUA_MULTRET, 1); + lua_pushboolean(L, (status == 0)); + lua_replace(L, 1); + return lua_gettop(L); /* return status + all results */ +} + + +static int luaB_tostring (lua_State *L) { + char buff[64]; + luaL_checkany(L, 1); + if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */ + return 1; /* use its value */ + switch (lua_type(L, 1)) { + case LUA_TNUMBER: + lua_pushstring(L, lua_tostring(L, 1)); + return 1; + case LUA_TSTRING: + lua_pushvalue(L, 1); + return 1; + case LUA_TBOOLEAN: + lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false")); + return 1; + case LUA_TTABLE: + sprintf(buff, "table: %p", lua_topointer(L, 1)); + break; + case LUA_TFUNCTION: + sprintf(buff, "function: %p", lua_topointer(L, 1)); + break; + case LUA_TUSERDATA: + case LUA_TLIGHTUSERDATA: + sprintf(buff, "userdata: %p", lua_touserdata(L, 1)); + break; + case LUA_TTHREAD: + sprintf(buff, "thread: %p", (void *)lua_tothread(L, 1)); + break; + case LUA_TNIL: + lua_pushliteral(L, "nil"); + return 1; + } + lua_pushstring(L, buff); + return 1; +} + + +static int luaB_newproxy (lua_State *L) { + lua_settop(L, 1); + lua_newuserdata(L, 0); /* create proxy */ + if (lua_toboolean(L, 1) == 0) + return 1; /* no metatable */ + else if (lua_isboolean(L, 1)) { + lua_newtable(L); /* create a new metatable `m' ... */ + lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */ + lua_pushboolean(L, 1); + lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */ + } + else { + int validproxy = 0; /* to check if weaktable[metatable(u)] == true */ + if (lua_getmetatable(L, 1)) { + lua_rawget(L, lua_upvalueindex(1)); + validproxy = lua_toboolean(L, -1); + lua_pop(L, 1); /* remove value */ + } + luaL_argcheck(L, validproxy, 1, "boolean or proxy expected"); + lua_getmetatable(L, 1); /* metatable is valid; get it */ + } + lua_setmetatable(L, 2); + return 1; +} + + +/* +** {====================================================== +** `require' function +** ======================================================= +*/ + + +/* name of global that holds table with loaded packages */ +#define REQTAB "_LOADED" + +/* name of global that holds the search path for packages */ +#define LUA_PATH "LUA_PATH" + +#ifndef LUA_PATH_SEP +#define LUA_PATH_SEP ';' +#endif + +#ifndef LUA_PATH_MARK +#define LUA_PATH_MARK '?' +#endif + +#ifndef LUA_PATH_DEFAULT +#define LUA_PATH_DEFAULT "?;?.lua" +#endif + + +static const char *getpath (lua_State *L) { + const char *path; + lua_getglobal(L, LUA_PATH); /* try global variable */ + path = lua_tostring(L, -1); + lua_pop(L, 1); + if (path) return path; + path = getenv(LUA_PATH); /* else try environment variable */ + if (path) return path; + return LUA_PATH_DEFAULT; /* else use default */ +} + + +static const char *pushnextpath (lua_State *L, const char *path) { + const char *l; + if (*path == '\0') return NULL; /* no more paths */ + if (*path == LUA_PATH_SEP) path++; /* skip separator */ + l = strchr(path, LUA_PATH_SEP); /* find next separator */ + if (l == NULL) l = path+strlen(path); + lua_pushlstring(L, path, l - path); /* directory name */ + return l; +} + + +static void pushcomposename (lua_State *L) { + const char *path = lua_tostring(L, -1); + const char *wild; + int n = 1; + while ((wild = strchr(path, LUA_PATH_MARK)) != NULL) { + /* is there stack space for prefix, name, and eventual last sufix? */ + luaL_checkstack(L, 3, "too many marks in a path component"); + lua_pushlstring(L, path, wild - path); /* push prefix */ + lua_pushvalue(L, 1); /* push package name (in place of MARK) */ + path = wild + 1; /* continue after MARK */ + n += 2; + } + lua_pushstring(L, path); /* push last sufix (`n' already includes this) */ + lua_concat(L, n); +} + + +static int luaB_require (lua_State *L) { + const char *path; + int status = LUA_ERRFILE; /* not found (yet) */ + luaL_checkstring(L, 1); + lua_settop(L, 1); + lua_getglobal(L, REQTAB); + if (!lua_istable(L, 2)) return luaL_error(L, "`" REQTAB "' is not a table"); + path = getpath(L); + lua_pushvalue(L, 1); /* check package's name in book-keeping table */ + lua_rawget(L, 2); + if (lua_toboolean(L, -1)) /* is it there? */ + return 1; /* package is already loaded; return its result */ + else { /* must load it */ + while (status == LUA_ERRFILE) { + lua_settop(L, 3); /* reset stack position */ + if ((path = pushnextpath(L, path)) == NULL) break; + pushcomposename(L); + status = luaL_loadfile(L, lua_tostring(L, -1)); /* try to load it */ + } + } + switch (status) { + case 0: { + lua_getglobal(L, "_REQUIREDNAME"); /* save previous name */ + lua_insert(L, -2); /* put it below function */ + lua_pushvalue(L, 1); + lua_setglobal(L, "_REQUIREDNAME"); /* set new name */ + lua_call(L, 0, 1); /* run loaded module */ + lua_insert(L, -2); /* put result below previous name */ + lua_setglobal(L, "_REQUIREDNAME"); /* reset to previous name */ + if (lua_isnil(L, -1)) { /* no/nil return? */ + lua_pushboolean(L, 1); + lua_replace(L, -2); /* replace to true */ + } + lua_pushvalue(L, 1); + lua_pushvalue(L, -2); + lua_rawset(L, 2); /* mark it as loaded */ + return 1; /* return value */ + } + case LUA_ERRFILE: { /* file not found */ + return luaL_error(L, "could not load package `%s' from path `%s'", + lua_tostring(L, 1), getpath(L)); + } + default: { + return luaL_error(L, "error loading package `%s' (%s)", + lua_tostring(L, 1), lua_tostring(L, -1)); + } + } +} + +/* }====================================================== */ + + +static const luaL_reg base_funcs[] = { + {"error", luaB_error}, + {"getmetatable", luaB_getmetatable}, + {"setmetatable", luaB_setmetatable}, + {"getfenv", luaB_getfenv}, + {"setfenv", luaB_setfenv}, + {"next", luaB_next}, + {"ipairs", luaB_ipairs}, + {"pairs", luaB_pairs}, + {"print", luaB_print}, + {"tonumber", luaB_tonumber}, + {"tostring", luaB_tostring}, + {"type", luaB_type}, + {"assert", luaB_assert}, + {"unpack", luaB_unpack}, + {"rawequal", luaB_rawequal}, + {"rawget", luaB_rawget}, + {"rawset", luaB_rawset}, + {"pcall", luaB_pcall}, + {"xpcall", luaB_xpcall}, + {"collectgarbage", luaB_collectgarbage}, + {"gcinfo", luaB_gcinfo}, + {"loadfile", luaB_loadfile}, + {"dofile", luaB_dofile}, + {"loadstring", luaB_loadstring}, + {"require", luaB_require}, + {NULL, NULL} +}; + + +/* +** {====================================================== +** Coroutine library +** ======================================================= +*/ + +static int auxresume (lua_State *L, lua_State *co, int narg) { + int status; + if (!lua_checkstack(co, narg)) + luaL_error(L, "too many arguments to resume"); + lua_xmove(L, co, narg); + status = lua_resume(co, narg); + if (status == 0) { + int nres = lua_gettop(co); + if (!lua_checkstack(L, nres)) + luaL_error(L, "too many results to resume"); + lua_xmove(co, L, nres); /* move yielded values */ + return nres; + } + else { + lua_xmove(co, L, 1); /* move error message */ + return -1; /* error flag */ + } +} + + +static int luaB_coresume (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + int r; + luaL_argcheck(L, co, 1, "coroutine expected"); + r = auxresume(L, co, lua_gettop(L) - 1); + if (r < 0) { + lua_pushboolean(L, 0); + lua_insert(L, -2); + return 2; /* return false + error message */ + } + else { + lua_pushboolean(L, 1); + lua_insert(L, -(r + 1)); + return r + 1; /* return true + `resume' returns */ + } +} + + +static int luaB_auxwrap (lua_State *L) { + lua_State *co = lua_tothread(L, lua_upvalueindex(1)); + int r = auxresume(L, co, lua_gettop(L)); + if (r < 0) { + if (lua_isstring(L, -1)) { /* error object is a string? */ + luaL_where(L, 1); /* add extra info */ + lua_insert(L, -2); + lua_concat(L, 2); + } + lua_error(L); /* propagate error */ + } + return r; +} + + +static int luaB_cocreate (lua_State *L) { + lua_State *NL = lua_newthread(L); + luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, + "Lua function expected"); + lua_pushvalue(L, 1); /* move function to top */ + lua_xmove(L, NL, 1); /* move function from L to NL */ + return 1; +} + + +static int luaB_cowrap (lua_State *L) { + luaB_cocreate(L); + lua_pushcclosure(L, luaB_auxwrap, 1); + return 1; +} + + +static int luaB_yield (lua_State *L) { + return lua_yield(L, lua_gettop(L)); +} + + +static int luaB_costatus (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + luaL_argcheck(L, co, 1, "coroutine expected"); + if (L == co) lua_pushliteral(L, "running"); + else { + lua_Debug ar; + if (lua_getstack(co, 0, &ar) == 0 && lua_gettop(co) == 0) + lua_pushliteral(L, "dead"); + else + lua_pushliteral(L, "suspended"); + } + return 1; +} + + +static const luaL_reg co_funcs[] = { + {"create", luaB_cocreate}, + {"wrap", luaB_cowrap}, + {"resume", luaB_coresume}, + {"yield", luaB_yield}, + {"status", luaB_costatus}, + {NULL, NULL} +}; + +/* }====================================================== */ + + + +static void base_open (lua_State *L) { + lua_pushliteral(L, "_G"); + lua_pushvalue(L, LUA_GLOBALSINDEX); + luaL_openlib(L, NULL, base_funcs, 0); /* open lib into global table */ + lua_pushliteral(L, "_VERSION"); + lua_pushliteral(L, LUA_VERSION); + lua_rawset(L, -3); /* set global _VERSION */ + /* `newproxy' needs a weaktable as upvalue */ + lua_pushliteral(L, "newproxy"); + lua_newtable(L); /* new table `w' */ + lua_pushvalue(L, -1); /* `w' will be its own metatable */ + lua_setmetatable(L, -2); + lua_pushliteral(L, "__mode"); + lua_pushliteral(L, "k"); + lua_rawset(L, -3); /* metatable(w).__mode = "k" */ + lua_pushcclosure(L, luaB_newproxy, 1); + lua_rawset(L, -3); /* set global `newproxy' */ + lua_rawset(L, -1); /* set global _G */ +} + + +LUALIB_API int luaopen_base (lua_State *L) { + base_open(L); + luaL_openlib(L, LUA_COLIBNAME, co_funcs, 0); + lua_newtable(L); + lua_setglobal(L, REQTAB); + return 0; +} + diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ldblib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ldblib.c index 8424a37847..55d4d042c7 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ldblib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ldblib.c @@ -1,309 +1,309 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include -#include -#include - -#define ldblib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - - -static void settabss (lua_State *L, const char *i, const char *v) { - lua_pushstring(L, i); - lua_pushstring(L, v); - lua_rawset(L, -3); -} - - -static void settabsi (lua_State *L, const char *i, int v) { - lua_pushstring(L, i); - lua_pushnumber(L, (lua_Number)v); - lua_rawset(L, -3); -} - - -static int getinfo (lua_State *L) { - lua_Debug ar; - const char *options = luaL_optstring(L, 2, "flnSu"); - if (lua_isnumber(L, 1)) { - if (!lua_getstack(L, (int)(lua_tonumber(L, 1)), &ar)) { - lua_pushnil(L); /* level out of range */ - return 1; - } - } - else if (lua_isfunction(L, 1)) { - lua_pushfstring(L, ">%s", options); - options = lua_tostring(L, -1); - lua_pushvalue(L, 1); - } - else - return luaL_argerror(L, 1, "function or level expected"); - if (!lua_getinfo(L, options, &ar)) - return luaL_argerror(L, 2, "invalid option"); - lua_newtable(L); - for (; *options; options++) { - switch (*options) { - case 'S': - settabss(L, "source", ar.source); - settabss(L, "short_src", ar.short_src); - settabsi(L, "linedefined", ar.linedefined); - settabss(L, "what", ar.what); - break; - case 'l': - settabsi(L, "currentline", ar.currentline); - break; - case 'u': - settabsi(L, "nups", ar.nups); - break; - case 'n': - settabss(L, "name", ar.name); - settabss(L, "namewhat", ar.namewhat); - break; - case 'f': - lua_pushliteral(L, "func"); - lua_pushvalue(L, -3); - lua_rawset(L, -3); - break; - } - } - return 1; /* return table */ -} - - -static int getlocal (lua_State *L) { - lua_Debug ar; - const char *name; - if (!lua_getstack(L, luaL_checkint(L, 1), &ar)) /* level out of range? */ - return luaL_argerror(L, 1, "level out of range"); - name = lua_getlocal(L, &ar, luaL_checkint(L, 2)); - if (name) { - lua_pushstring(L, name); - lua_pushvalue(L, -2); - return 2; - } - else { - lua_pushnil(L); - return 1; - } -} - - -static int setlocal (lua_State *L) { - lua_Debug ar; - if (!lua_getstack(L, luaL_checkint(L, 1), &ar)) /* level out of range? */ - return luaL_argerror(L, 1, "level out of range"); - luaL_checkany(L, 3); - lua_pushstring(L, lua_setlocal(L, &ar, luaL_checkint(L, 2))); - return 1; -} - - -static int auxupvalue (lua_State *L, int get) { - const char *name; - int n = luaL_checkint(L, 2); - luaL_checktype(L, 1, LUA_TFUNCTION); - if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */ - name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); - if (name == NULL) return 0; - lua_pushstring(L, name); - lua_insert(L, -(get+1)); - return get + 1; -} - - -static int getupvalue (lua_State *L) { - return auxupvalue(L, 1); -} - - -static int setupvalue (lua_State *L) { - luaL_checkany(L, 3); - return auxupvalue(L, 0); -} - - - -static const char KEY_HOOK = 'h'; - - -static void hookf (lua_State *L, lua_Debug *ar) { - static const char *const hooknames[] = - {"call", "return", "line", "count", "tail return"}; - lua_pushlightuserdata(L, (void *)&KEY_HOOK); - lua_rawget(L, LUA_REGISTRYINDEX); - if (lua_isfunction(L, -1)) { - lua_pushstring(L, hooknames[(int)ar->event]); - if (ar->currentline >= 0) - lua_pushnumber(L, (lua_Number)ar->currentline); - else lua_pushnil(L); - lua_assert(lua_getinfo(L, "lS", ar)); - lua_call(L, 2, 0); - } - else - lua_pop(L, 1); /* pop result from gettable */ -} - - -static int makemask (const char *smask, int count) { - int mask = 0; - if (strchr(smask, 'c')) mask |= LUA_MASKCALL; - if (strchr(smask, 'r')) mask |= LUA_MASKRET; - if (strchr(smask, 'l')) mask |= LUA_MASKLINE; - if (count > 0) mask |= LUA_MASKCOUNT; - return mask; -} - - -static char *unmakemask (int mask, char *smask) { - int i = 0; - if (mask & LUA_MASKCALL) smask[i++] = 'c'; - if (mask & LUA_MASKRET) smask[i++] = 'r'; - if (mask & LUA_MASKLINE) smask[i++] = 'l'; - smask[i] = '\0'; - return smask; -} - - -static int sethook (lua_State *L) { - if (lua_isnoneornil(L, 1)) { - lua_settop(L, 1); - lua_sethook(L, NULL, 0, 0); /* turn off hooks */ - } - else { - const char *smask = luaL_checkstring(L, 2); - int count = luaL_optint(L, 3, 0); - luaL_checktype(L, 1, LUA_TFUNCTION); - lua_sethook(L, hookf, makemask(smask, count), count); - } - lua_pushlightuserdata(L, (void *)&KEY_HOOK); - lua_pushvalue(L, 1); - lua_rawset(L, LUA_REGISTRYINDEX); /* set new hook */ - return 0; -} - - -static int gethook (lua_State *L) { - char buff[5]; - int mask = lua_gethookmask(L); - lua_Hook hook = lua_gethook(L); - if (hook != NULL && hook != hookf) /* external hook? */ - lua_pushliteral(L, "external hook"); - else { - lua_pushlightuserdata(L, (void *)&KEY_HOOK); - lua_rawget(L, LUA_REGISTRYINDEX); /* get hook */ - } - lua_pushstring(L, unmakemask(mask, buff)); - lua_pushnumber(L, (lua_Number)lua_gethookcount(L)); - return 3; -} - - -static int debug (lua_State *L) { - for (;;) { - char buffer[250]; - fputs("lua_debug> ", stderr); - if (fgets(buffer, sizeof(buffer), stdin) == 0 || - strcmp(buffer, "cont\n") == 0) - return 0; - lua_dostring(L, buffer); - lua_settop(L, 0); /* remove eventual returns */ - } -} - - -#define LEVELS1 12 /* size of the first part of the stack */ -#define LEVELS2 10 /* size of the second part of the stack */ - -static int errorfb (lua_State *L) { - int level = 1; /* skip level 0 (it's this function) */ - int firstpart = 1; /* still before eventual `...' */ - lua_Debug ar; - if (lua_gettop(L) == 0) - lua_pushliteral(L, ""); - else if (!lua_isstring(L, 1)) return 1; /* no string message */ - else lua_pushliteral(L, "\n"); - lua_pushliteral(L, "stack traceback:"); - while (lua_getstack(L, level++, &ar)) { - if (level > LEVELS1 && firstpart) { - /* no more than `LEVELS2' more levels? */ - if (!lua_getstack(L, level+LEVELS2, &ar)) - level--; /* keep going */ - else { - lua_pushliteral(L, "\n\t..."); /* too many levels */ - while (lua_getstack(L, level+LEVELS2, &ar)) /* find last levels */ - level++; - } - firstpart = 0; - continue; - } - lua_pushliteral(L, "\n\t"); - lua_getinfo(L, "Snl", &ar); - lua_pushfstring(L, "%s:", ar.short_src); - if (ar.currentline > 0) - lua_pushfstring(L, "%d:", ar.currentline); - switch (*ar.namewhat) { - case 'g': /* global */ - case 'l': /* local */ - case 'f': /* field */ - case 'm': /* method */ - lua_pushfstring(L, " in function `%s'", ar.name); - break; - default: { - if (*ar.what == 'm') /* main? */ - lua_pushfstring(L, " in main chunk"); - else if (*ar.what == 'C' || *ar.what == 't') - lua_pushliteral(L, " ?"); /* C function or tail call */ - else - lua_pushfstring(L, " in function <%s:%d>", - ar.short_src, ar.linedefined); - } - } - lua_concat(L, lua_gettop(L)); - } - lua_concat(L, lua_gettop(L)); - return 1; -} - - -static const luaL_reg dblib[] = { - {"getlocal", getlocal}, - {"getinfo", getinfo}, - {"gethook", gethook}, - {"getupvalue", getupvalue}, - {"sethook", sethook}, - {"setlocal", setlocal}, - {"setupvalue", setupvalue}, - {"debug", debug}, - {"traceback", errorfb}, - {NULL, NULL} -}; - - -LUALIB_API int luaopen_debug (lua_State *L) { - luaL_openlib(L, LUA_DBLIBNAME, dblib, 0); - lua_pushliteral(L, "_TRACEBACK"); - lua_pushcfunction(L, errorfb); - lua_settable(L, LUA_GLOBALSINDEX); - return 1; -} - +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include +#include +#include + +#define ldblib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + +static void settabss (lua_State *L, const char *i, const char *v) { + lua_pushstring(L, i); + lua_pushstring(L, v); + lua_rawset(L, -3); +} + + +static void settabsi (lua_State *L, const char *i, int v) { + lua_pushstring(L, i); + lua_pushnumber(L, (lua_Number)v); + lua_rawset(L, -3); +} + + +static int getinfo (lua_State *L) { + lua_Debug ar; + const char *options = luaL_optstring(L, 2, "flnSu"); + if (lua_isnumber(L, 1)) { + if (!lua_getstack(L, (int)(lua_tonumber(L, 1)), &ar)) { + lua_pushnil(L); /* level out of range */ + return 1; + } + } + else if (lua_isfunction(L, 1)) { + lua_pushfstring(L, ">%s", options); + options = lua_tostring(L, -1); + lua_pushvalue(L, 1); + } + else + return luaL_argerror(L, 1, "function or level expected"); + if (!lua_getinfo(L, options, &ar)) + return luaL_argerror(L, 2, "invalid option"); + lua_newtable(L); + for (; *options; options++) { + switch (*options) { + case 'S': + settabss(L, "source", ar.source); + settabss(L, "short_src", ar.short_src); + settabsi(L, "linedefined", ar.linedefined); + settabss(L, "what", ar.what); + break; + case 'l': + settabsi(L, "currentline", ar.currentline); + break; + case 'u': + settabsi(L, "nups", ar.nups); + break; + case 'n': + settabss(L, "name", ar.name); + settabss(L, "namewhat", ar.namewhat); + break; + case 'f': + lua_pushliteral(L, "func"); + lua_pushvalue(L, -3); + lua_rawset(L, -3); + break; + } + } + return 1; /* return table */ +} + + +static int getlocal (lua_State *L) { + lua_Debug ar; + const char *name; + if (!lua_getstack(L, luaL_checkint(L, 1), &ar)) /* level out of range? */ + return luaL_argerror(L, 1, "level out of range"); + name = lua_getlocal(L, &ar, luaL_checkint(L, 2)); + if (name) { + lua_pushstring(L, name); + lua_pushvalue(L, -2); + return 2; + } + else { + lua_pushnil(L); + return 1; + } +} + + +static int setlocal (lua_State *L) { + lua_Debug ar; + if (!lua_getstack(L, luaL_checkint(L, 1), &ar)) /* level out of range? */ + return luaL_argerror(L, 1, "level out of range"); + luaL_checkany(L, 3); + lua_pushstring(L, lua_setlocal(L, &ar, luaL_checkint(L, 2))); + return 1; +} + + +static int auxupvalue (lua_State *L, int get) { + const char *name; + int n = luaL_checkint(L, 2); + luaL_checktype(L, 1, LUA_TFUNCTION); + if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */ + name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); + if (name == NULL) return 0; + lua_pushstring(L, name); + lua_insert(L, -(get+1)); + return get + 1; +} + + +static int getupvalue (lua_State *L) { + return auxupvalue(L, 1); +} + + +static int setupvalue (lua_State *L) { + luaL_checkany(L, 3); + return auxupvalue(L, 0); +} + + + +static const char KEY_HOOK = 'h'; + + +static void hookf (lua_State *L, lua_Debug *ar) { + static const char *const hooknames[] = + {"call", "return", "line", "count", "tail return"}; + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_rawget(L, LUA_REGISTRYINDEX); + if (lua_isfunction(L, -1)) { + lua_pushstring(L, hooknames[(int)ar->event]); + if (ar->currentline >= 0) + lua_pushnumber(L, (lua_Number)ar->currentline); + else lua_pushnil(L); + lua_assert(lua_getinfo(L, "lS", ar)); + lua_call(L, 2, 0); + } + else + lua_pop(L, 1); /* pop result from gettable */ +} + + +static int makemask (const char *smask, int count) { + int mask = 0; + if (strchr(smask, 'c')) mask |= LUA_MASKCALL; + if (strchr(smask, 'r')) mask |= LUA_MASKRET; + if (strchr(smask, 'l')) mask |= LUA_MASKLINE; + if (count > 0) mask |= LUA_MASKCOUNT; + return mask; +} + + +static char *unmakemask (int mask, char *smask) { + int i = 0; + if (mask & LUA_MASKCALL) smask[i++] = 'c'; + if (mask & LUA_MASKRET) smask[i++] = 'r'; + if (mask & LUA_MASKLINE) smask[i++] = 'l'; + smask[i] = '\0'; + return smask; +} + + +static int sethook (lua_State *L) { + if (lua_isnoneornil(L, 1)) { + lua_settop(L, 1); + lua_sethook(L, NULL, 0, 0); /* turn off hooks */ + } + else { + const char *smask = luaL_checkstring(L, 2); + int count = luaL_optint(L, 3, 0); + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_sethook(L, hookf, makemask(smask, count), count); + } + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_pushvalue(L, 1); + lua_rawset(L, LUA_REGISTRYINDEX); /* set new hook */ + return 0; +} + + +static int gethook (lua_State *L) { + char buff[5]; + int mask = lua_gethookmask(L); + lua_Hook hook = lua_gethook(L); + if (hook != NULL && hook != hookf) /* external hook? */ + lua_pushliteral(L, "external hook"); + else { + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_rawget(L, LUA_REGISTRYINDEX); /* get hook */ + } + lua_pushstring(L, unmakemask(mask, buff)); + lua_pushnumber(L, (lua_Number)lua_gethookcount(L)); + return 3; +} + + +static int debug (lua_State *L) { + for (;;) { + char buffer[250]; + fputs("lua_debug> ", stderr); + if (fgets(buffer, sizeof(buffer), stdin) == 0 || + strcmp(buffer, "cont\n") == 0) + return 0; + lua_dostring(L, buffer); + lua_settop(L, 0); /* remove eventual returns */ + } +} + + +#define LEVELS1 12 /* size of the first part of the stack */ +#define LEVELS2 10 /* size of the second part of the stack */ + +static int errorfb (lua_State *L) { + int level = 1; /* skip level 0 (it's this function) */ + int firstpart = 1; /* still before eventual `...' */ + lua_Debug ar; + if (lua_gettop(L) == 0) + lua_pushliteral(L, ""); + else if (!lua_isstring(L, 1)) return 1; /* no string message */ + else lua_pushliteral(L, "\n"); + lua_pushliteral(L, "stack traceback:"); + while (lua_getstack(L, level++, &ar)) { + if (level > LEVELS1 && firstpart) { + /* no more than `LEVELS2' more levels? */ + if (!lua_getstack(L, level+LEVELS2, &ar)) + level--; /* keep going */ + else { + lua_pushliteral(L, "\n\t..."); /* too many levels */ + while (lua_getstack(L, level+LEVELS2, &ar)) /* find last levels */ + level++; + } + firstpart = 0; + continue; + } + lua_pushliteral(L, "\n\t"); + lua_getinfo(L, "Snl", &ar); + lua_pushfstring(L, "%s:", ar.short_src); + if (ar.currentline > 0) + lua_pushfstring(L, "%d:", ar.currentline); + switch (*ar.namewhat) { + case 'g': /* global */ + case 'l': /* local */ + case 'f': /* field */ + case 'm': /* method */ + lua_pushfstring(L, " in function `%s'", ar.name); + break; + default: { + if (*ar.what == 'm') /* main? */ + lua_pushfstring(L, " in main chunk"); + else if (*ar.what == 'C' || *ar.what == 't') + lua_pushliteral(L, " ?"); /* C function or tail call */ + else + lua_pushfstring(L, " in function <%s:%d>", + ar.short_src, ar.linedefined); + } + } + lua_concat(L, lua_gettop(L)); + } + lua_concat(L, lua_gettop(L)); + return 1; +} + + +static const luaL_reg dblib[] = { + {"getlocal", getlocal}, + {"getinfo", getinfo}, + {"gethook", gethook}, + {"getupvalue", getupvalue}, + {"sethook", sethook}, + {"setlocal", setlocal}, + {"setupvalue", setupvalue}, + {"debug", debug}, + {"traceback", errorfb}, + {NULL, NULL} +}; + + +LUALIB_API int luaopen_debug (lua_State *L) { + luaL_openlib(L, LUA_DBLIBNAME, dblib, 0); + lua_pushliteral(L, "_TRACEBACK"); + lua_pushcfunction(L, errorfb); + lua_settable(L, LUA_GLOBALSINDEX); + return 1; +} + diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/liolib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/liolib.c index 1aa0cc5bc3..01281e2733 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/liolib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/liolib.c @@ -1,760 +1,760 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include -#include -#include -#include -#include -#include - -#define liolib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - - -/* -** by default, gcc does not get `tmpname' -*/ -#ifndef USE_TMPNAME -#ifdef __GNUC__ -#define USE_TMPNAME 0 -#else -#define USE_TMPNAME 1 -#endif -#endif - - -/* -** by default, posix systems get `popen' -*/ -#ifndef USE_POPEN -#ifdef _POSIX_C_SOURCE -#if _POSIX_C_SOURCE >= 2 -#define USE_POPEN 1 -#endif -#endif -#endif - -#ifndef USE_POPEN -#define USE_POPEN 0 -#endif - - - - -/* -** {====================================================== -** FILE Operations -** ======================================================= -*/ - - -#if !USE_POPEN -#define pclose(f) (-1) -#endif - - -#define FILEHANDLE "FILE*" - -#define IO_INPUT "_input" -#define IO_OUTPUT "_output" - - -static int pushresult (lua_State *L, int i, const char *filename) { - if (i) { - lua_pushboolean(L, 1); - return 1; - } - else { - lua_pushnil(L); - if (filename) - lua_pushfstring(L, "%s: %s", filename, strerror(errno)); - else - lua_pushfstring(L, "%s", strerror(errno)); - lua_pushnumber(L, errno); - return 3; - } -} - - -static FILE **topfile (lua_State *L, int findex) { - FILE **f = (FILE **)luaL_checkudata(L, findex, FILEHANDLE); - if (f == NULL) luaL_argerror(L, findex, "bad file"); - return f; -} - - -static int io_type (lua_State *L) { - FILE **f = (FILE **)luaL_checkudata(L, 1, FILEHANDLE); - if (f == NULL) lua_pushnil(L); - else if (*f == NULL) - lua_pushliteral(L, "closed file"); - else - lua_pushliteral(L, "file"); - return 1; -} - - -static FILE *tofile (lua_State *L, int findex) { - FILE **f = topfile(L, findex); - if (*f == NULL) - luaL_error(L, "attempt to use a closed file"); - return *f; -} - - - -/* -** When creating file handles, always creates a `closed' file handle -** before opening the actual file; so, if there is a memory error, the -** file is not left opened. -*/ -static FILE **newfile (lua_State *L) { - FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *)); - *pf = NULL; /* file handle is currently `closed' */ - luaL_getmetatable(L, FILEHANDLE); - lua_setmetatable(L, -2); - return pf; -} - - -/* -** assumes that top of the stack is the `io' library, and next is -** the `io' metatable -*/ -static void registerfile (lua_State *L, FILE *f, const char *name, - const char *impname) { - lua_pushstring(L, name); - *newfile(L) = f; - if (impname) { - lua_pushstring(L, impname); - lua_pushvalue(L, -2); - lua_settable(L, -6); /* metatable[impname] = file */ - } - lua_settable(L, -3); /* io[name] = file */ -} - - -static int aux_close (lua_State *L) { - FILE *f = tofile(L, 1); - if (f == stdin || f == stdout || f == stderr) - return 0; /* file cannot be closed */ - else { - int ok = (pclose(f) != -1) || (fclose(f) == 0); - if (ok) - *(FILE **)lua_touserdata(L, 1) = NULL; /* mark file as closed */ - return ok; - } -} - - -static int io_close (lua_State *L) { - if (lua_isnone(L, 1)) { - lua_pushstring(L, IO_OUTPUT); - lua_rawget(L, lua_upvalueindex(1)); - } - return pushresult(L, aux_close(L), NULL); -} - - -static int io_gc (lua_State *L) { - FILE **f = topfile(L, 1); - if (*f != NULL) /* ignore closed files */ - aux_close(L); - return 0; -} - - -static int io_tostring (lua_State *L) { - char buff[32]; - FILE **f = topfile(L, 1); - if (*f == NULL) - strcpy(buff, "closed"); - else - sprintf(buff, "%p", lua_touserdata(L, 1)); - lua_pushfstring(L, "file (%s)", buff); - return 1; -} - - -static int io_open (lua_State *L) { - const char *filename = luaL_checkstring(L, 1); - const char *mode = luaL_optstring(L, 2, "r"); - FILE **pf = newfile(L); - *pf = fopen(filename, mode); - return (*pf == NULL) ? pushresult(L, 0, filename) : 1; -} - - -static int io_popen (lua_State *L) { -#if !USE_POPEN - luaL_error(L, "`popen' not supported"); - return 0; -#else - const char *filename = luaL_checkstring(L, 1); - const char *mode = luaL_optstring(L, 2, "r"); - FILE **pf = newfile(L); - *pf = popen(filename, mode); - return (*pf == NULL) ? pushresult(L, 0, filename) : 1; -#endif -} - - -static int io_tmpfile (lua_State *L) { - FILE **pf = newfile(L); - *pf = tmpfile(); - return (*pf == NULL) ? pushresult(L, 0, NULL) : 1; -} - - -static FILE *getiofile (lua_State *L, const char *name) { - lua_pushstring(L, name); - lua_rawget(L, lua_upvalueindex(1)); - return tofile(L, -1); -} - - -static int g_iofile (lua_State *L, const char *name, const char *mode) { - if (!lua_isnoneornil(L, 1)) { - const char *filename = lua_tostring(L, 1); - lua_pushstring(L, name); - if (filename) { - FILE **pf = newfile(L); - *pf = fopen(filename, mode); - if (*pf == NULL) { - lua_pushfstring(L, "%s: %s", filename, strerror(errno)); - luaL_argerror(L, 1, lua_tostring(L, -1)); - } - } - else { - tofile(L, 1); /* check that it's a valid file handle */ - lua_pushvalue(L, 1); - } - lua_rawset(L, lua_upvalueindex(1)); - } - /* return current value */ - lua_pushstring(L, name); - lua_rawget(L, lua_upvalueindex(1)); - return 1; -} - - -static int io_input (lua_State *L) { - return g_iofile(L, IO_INPUT, "r"); -} - - -static int io_output (lua_State *L) { - return g_iofile(L, IO_OUTPUT, "w"); -} - - -static int io_readline (lua_State *L); - - -static void aux_lines (lua_State *L, int idx, int close) { - lua_pushliteral(L, FILEHANDLE); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_pushvalue(L, idx); - lua_pushboolean(L, close); /* close/not close file when finished */ - lua_pushcclosure(L, io_readline, 3); -} - - -static int f_lines (lua_State *L) { - tofile(L, 1); /* check that it's a valid file handle */ - aux_lines(L, 1, 0); - return 1; -} - - -static int io_lines (lua_State *L) { - if (lua_isnoneornil(L, 1)) { /* no arguments? */ - lua_pushstring(L, IO_INPUT); - lua_rawget(L, lua_upvalueindex(1)); /* will iterate over default input */ - return f_lines(L); - } - else { - const char *filename = luaL_checkstring(L, 1); - FILE **pf = newfile(L); - *pf = fopen(filename, "r"); - luaL_argcheck(L, *pf, 1, strerror(errno)); - aux_lines(L, lua_gettop(L), 1); - return 1; - } -} - - -/* -** {====================================================== -** READ -** ======================================================= -*/ - - -static int read_number (lua_State *L, FILE *f) { - lua_Number d; - if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { - lua_pushnumber(L, d); - return 1; - } - else return 0; /* read fails */ -} - - -static int test_eof (lua_State *L, FILE *f) { - int c = getc(f); - ungetc(c, f); - lua_pushlstring(L, NULL, 0); - return (c != EOF); -} - - -static int read_line (lua_State *L, FILE *f) { - luaL_Buffer b; - luaL_buffinit(L, &b); - for (;;) { - size_t l; - char *p = luaL_prepbuffer(&b); - if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ - luaL_pushresult(&b); /* close buffer */ - return (lua_strlen(L, -1) > 0); /* check whether read something */ - } - l = strlen(p); - if (p[l-1] != '\n') - luaL_addsize(&b, l); - else { - luaL_addsize(&b, l - 1); /* do not include `eol' */ - luaL_pushresult(&b); /* close buffer */ - return 1; /* read at least an `eol' */ - } - } -} - - -static int read_chars (lua_State *L, FILE *f, size_t n) { - size_t rlen; /* how much to read */ - size_t nr; /* number of chars actually read */ - luaL_Buffer b; - luaL_buffinit(L, &b); - rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ - do { - char *p = luaL_prepbuffer(&b); - if (rlen > n) rlen = n; /* cannot read more than asked */ - nr = fread(p, sizeof(char), rlen, f); - luaL_addsize(&b, nr); - n -= nr; /* still have to read `n' chars */ - } while (n > 0 && nr == rlen); /* until end of count or eof */ - luaL_pushresult(&b); /* close buffer */ - return (n == 0 || lua_strlen(L, -1) > 0); -} - - -static int g_read (lua_State *L, FILE *f, int first) { - int nargs = lua_gettop(L) - 1; - int success; - int n; - if (nargs == 0) { /* no arguments? */ - success = read_line(L, f); - n = first+1; /* to return 1 result */ - } - else { /* ensure stack space for all results and for auxlib's buffer */ - luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); - success = 1; - for (n = first; nargs-- && success; n++) { - if (lua_type(L, n) == LUA_TNUMBER) { - size_t l = (size_t)lua_tonumber(L, n); - success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); - } - else { - const char *p = lua_tostring(L, n); - luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); - switch (p[1]) { - case 'n': /* number */ - success = read_number(L, f); - break; - case 'l': /* line */ - success = read_line(L, f); - break; - case 'a': /* file */ - read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */ - success = 1; /* always success */ - break; - case 'w': /* word */ - return luaL_error(L, "obsolete option `*w' to `read'"); - default: - return luaL_argerror(L, n, "invalid format"); - } - } - } - } - if (!success) { - lua_pop(L, 1); /* remove last result */ - lua_pushnil(L); /* push nil instead */ - } - return n - first; -} - - -static int io_read (lua_State *L) { - return g_read(L, getiofile(L, IO_INPUT), 1); -} - - -static int f_read (lua_State *L) { - return g_read(L, tofile(L, 1), 2); -} - - -static int io_readline (lua_State *L) { - FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(2)); - if (f == NULL) /* file is already closed? */ - luaL_error(L, "file is already closed"); - if (read_line(L, f)) return 1; - else { /* EOF */ - if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */ - lua_settop(L, 0); - lua_pushvalue(L, lua_upvalueindex(2)); - aux_close(L); /* close it */ - } - return 0; - } -} - -/* }====================================================== */ - - -static int g_write (lua_State *L, FILE *f, int arg) { - int nargs = lua_gettop(L) - 1; - int status = 1; - for (; nargs--; arg++) { - if (lua_type(L, arg) == LUA_TNUMBER) { - /* optimization: could be done exactly as for strings */ - status = status && - fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; - } - else { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); - status = status && (fwrite(s, sizeof(char), l, f) == l); - } - } - return pushresult(L, status, NULL); -} - - -static int io_write (lua_State *L) { - return g_write(L, getiofile(L, IO_OUTPUT), 1); -} - - -static int f_write (lua_State *L) { - return g_write(L, tofile(L, 1), 2); -} - - -static int f_seek (lua_State *L) { - static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; - static const char *const modenames[] = {"set", "cur", "end", NULL}; - FILE *f = tofile(L, 1); - int op = luaL_findstring(luaL_optstring(L, 2, "cur"), modenames); - long offset = luaL_optlong(L, 3, 0); - luaL_argcheck(L, op != -1, 2, "invalid mode"); - op = fseek(f, offset, mode[op]); - if (op) - return pushresult(L, 0, NULL); /* error */ - else { - lua_pushnumber(L, ftell(f)); - return 1; - } -} - - -static int io_flush (lua_State *L) { - return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); -} - - -static int f_flush (lua_State *L) { - return pushresult(L, fflush(tofile(L, 1)) == 0, NULL); -} - - -static const luaL_reg iolib[] = { - {"input", io_input}, - {"output", io_output}, - {"lines", io_lines}, - {"close", io_close}, - {"flush", io_flush}, - {"open", io_open}, - {"popen", io_popen}, - {"read", io_read}, - {"tmpfile", io_tmpfile}, - {"type", io_type}, - {"write", io_write}, - {NULL, NULL} -}; - - -static const luaL_reg flib[] = { - {"flush", f_flush}, - {"read", f_read}, - {"lines", f_lines}, - {"seek", f_seek}, - {"write", f_write}, - {"close", io_close}, - {"__gc", io_gc}, - {"__tostring", io_tostring}, - {NULL, NULL} -}; - - -static void createmeta (lua_State *L) { - luaL_newmetatable(L, FILEHANDLE); /* create new metatable for file handles */ - /* file methods */ - lua_pushliteral(L, "__index"); - lua_pushvalue(L, -2); /* push metatable */ - lua_rawset(L, -3); /* metatable.__index = metatable */ - luaL_openlib(L, NULL, flib, 0); -} - -/* }====================================================== */ - - -/* -** {====================================================== -** Other O.S. Operations -** ======================================================= -*/ - -static int io_execute (lua_State *L) { - lua_pushnumber(L, system(luaL_checkstring(L, 1))); - return 1; -} - - -static int io_remove (lua_State *L) { - const char *filename = luaL_checkstring(L, 1); - return pushresult(L, remove(filename) == 0, filename); -} - - -static int io_rename (lua_State *L) { - const char *fromname = luaL_checkstring(L, 1); - const char *toname = luaL_checkstring(L, 2); - return pushresult(L, rename(fromname, toname) == 0, fromname); -} - - -static int io_tmpname (lua_State *L) { -#if !USE_TMPNAME - luaL_error(L, "`tmpname' not supported"); - return 0; -#else - char buff[L_tmpnam]; - if (tmpnam(buff) != buff) - return luaL_error(L, "unable to generate a unique filename in `tmpname'"); - lua_pushstring(L, buff); - return 1; -#endif -} - - -static int io_getenv (lua_State *L) { - lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ - return 1; -} - - -static int io_clock (lua_State *L) { - lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); - return 1; -} - - -/* -** {====================================================== -** Time/Date operations -** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, -** wday=%w+1, yday=%j, isdst=? } -** ======================================================= -*/ - -static void setfield (lua_State *L, const char *key, int value) { - lua_pushstring(L, key); - lua_pushnumber(L, value); - lua_rawset(L, -3); -} - -static void setboolfield (lua_State *L, const char *key, int value) { - lua_pushstring(L, key); - lua_pushboolean(L, value); - lua_rawset(L, -3); -} - -static int getboolfield (lua_State *L, const char *key) { - int res; - lua_pushstring(L, key); - lua_gettable(L, -2); - res = lua_toboolean(L, -1); - lua_pop(L, 1); - return res; -} - - -static int getfield (lua_State *L, const char *key, int d) { - int res; - lua_pushstring(L, key); - lua_gettable(L, -2); - if (lua_isnumber(L, -1)) - res = (int)(lua_tonumber(L, -1)); - else { - if (d == -2) - return luaL_error(L, "field `%s' missing in date table", key); - res = d; - } - lua_pop(L, 1); - return res; -} - - -static int io_date (lua_State *L) { - const char *s = luaL_optstring(L, 1, "%c"); - time_t t = (time_t)(luaL_optnumber(L, 2, -1)); - struct tm *stm; - if (t == (time_t)(-1)) /* no time given? */ - t = time(NULL); /* use current time */ - if (*s == '!') { /* UTC? */ - stm = gmtime(&t); - s++; /* skip `!' */ - } - else - stm = localtime(&t); - if (stm == NULL) /* invalid date? */ - lua_pushnil(L); - else if (strcmp(s, "*t") == 0) { - lua_newtable(L); - setfield(L, "sec", stm->tm_sec); - setfield(L, "min", stm->tm_min); - setfield(L, "hour", stm->tm_hour); - setfield(L, "day", stm->tm_mday); - setfield(L, "month", stm->tm_mon+1); - setfield(L, "year", stm->tm_year+1900); - setfield(L, "wday", stm->tm_wday+1); - setfield(L, "yday", stm->tm_yday+1); - setboolfield(L, "isdst", stm->tm_isdst); - } - else { - char b[256]; - if (strftime(b, sizeof(b), s, stm)) - lua_pushstring(L, b); - else - return luaL_error(L, "`date' format too long"); - } - return 1; -} - - -static int io_time (lua_State *L) { - if (lua_isnoneornil(L, 1)) /* called without args? */ - lua_pushnumber(L, time(NULL)); /* return current time */ - else { - time_t t; - struct tm ts; - luaL_checktype(L, 1, LUA_TTABLE); - lua_settop(L, 1); /* make sure table is at the top */ - ts.tm_sec = getfield(L, "sec", 0); - ts.tm_min = getfield(L, "min", 0); - ts.tm_hour = getfield(L, "hour", 12); - ts.tm_mday = getfield(L, "day", -2); - ts.tm_mon = getfield(L, "month", -2) - 1; - ts.tm_year = getfield(L, "year", -2) - 1900; - ts.tm_isdst = getboolfield(L, "isdst"); - t = mktime(&ts); - if (t == (time_t)(-1)) - lua_pushnil(L); - else - lua_pushnumber(L, t); - } - return 1; -} - - -static int io_difftime (lua_State *L) { - lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), - (time_t)(luaL_optnumber(L, 2, 0)))); - return 1; -} - -/* }====================================================== */ - - -static int io_setloc (lua_State *L) { - static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, - LC_NUMERIC, LC_TIME}; - static const char *const catnames[] = {"all", "collate", "ctype", "monetary", - "numeric", "time", NULL}; - const char *l = lua_tostring(L, 1); - int op = luaL_findstring(luaL_optstring(L, 2, "all"), catnames); - luaL_argcheck(L, l || lua_isnoneornil(L, 1), 1, "string expected"); - luaL_argcheck(L, op != -1, 2, "invalid option"); - lua_pushstring(L, setlocale(cat[op], l)); - return 1; -} - - -static int io_exit (lua_State *L) { - exit(luaL_optint(L, 1, EXIT_SUCCESS)); - return 0; /* to avoid warnings */ -} - -static const luaL_reg syslib[] = { - {"clock", io_clock}, - {"date", io_date}, - {"difftime", io_difftime}, - {"execute", io_execute}, - {"exit", io_exit}, - {"getenv", io_getenv}, - {"remove", io_remove}, - {"rename", io_rename}, - {"setlocale", io_setloc}, - {"time", io_time}, - {"tmpname", io_tmpname}, - {NULL, NULL} -}; - -/* }====================================================== */ - - - -LUALIB_API int luaopen_io (lua_State *L) { - luaL_openlib(L, LUA_OSLIBNAME, syslib, 0); - createmeta(L); - lua_pushvalue(L, -1); - luaL_openlib(L, LUA_IOLIBNAME, iolib, 1); - /* put predefined file handles into `io' table */ - registerfile(L, stdin, "stdin", IO_INPUT); - registerfile(L, stdout, "stdout", IO_OUTPUT); - registerfile(L, stderr, "stderr", NULL); - return 1; -} - +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include +#include +#include +#include +#include +#include + +#define liolib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + +/* +** by default, gcc does not get `tmpname' +*/ +#ifndef USE_TMPNAME +#ifdef __GNUC__ +#define USE_TMPNAME 0 +#else +#define USE_TMPNAME 1 +#endif +#endif + + +/* +** by default, posix systems get `popen' +*/ +#ifndef USE_POPEN +#ifdef _POSIX_C_SOURCE +#if _POSIX_C_SOURCE >= 2 +#define USE_POPEN 1 +#endif +#endif +#endif + +#ifndef USE_POPEN +#define USE_POPEN 0 +#endif + + + + +/* +** {====================================================== +** FILE Operations +** ======================================================= +*/ + + +#if !USE_POPEN +#define pclose(f) (-1) +#endif + + +#define FILEHANDLE "FILE*" + +#define IO_INPUT "_input" +#define IO_OUTPUT "_output" + + +static int pushresult (lua_State *L, int i, const char *filename) { + if (i) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushnil(L); + if (filename) + lua_pushfstring(L, "%s: %s", filename, strerror(errno)); + else + lua_pushfstring(L, "%s", strerror(errno)); + lua_pushnumber(L, errno); + return 3; + } +} + + +static FILE **topfile (lua_State *L, int findex) { + FILE **f = (FILE **)luaL_checkudata(L, findex, FILEHANDLE); + if (f == NULL) luaL_argerror(L, findex, "bad file"); + return f; +} + + +static int io_type (lua_State *L) { + FILE **f = (FILE **)luaL_checkudata(L, 1, FILEHANDLE); + if (f == NULL) lua_pushnil(L); + else if (*f == NULL) + lua_pushliteral(L, "closed file"); + else + lua_pushliteral(L, "file"); + return 1; +} + + +static FILE *tofile (lua_State *L, int findex) { + FILE **f = topfile(L, findex); + if (*f == NULL) + luaL_error(L, "attempt to use a closed file"); + return *f; +} + + + +/* +** When creating file handles, always creates a `closed' file handle +** before opening the actual file; so, if there is a memory error, the +** file is not left opened. +*/ +static FILE **newfile (lua_State *L) { + FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *)); + *pf = NULL; /* file handle is currently `closed' */ + luaL_getmetatable(L, FILEHANDLE); + lua_setmetatable(L, -2); + return pf; +} + + +/* +** assumes that top of the stack is the `io' library, and next is +** the `io' metatable +*/ +static void registerfile (lua_State *L, FILE *f, const char *name, + const char *impname) { + lua_pushstring(L, name); + *newfile(L) = f; + if (impname) { + lua_pushstring(L, impname); + lua_pushvalue(L, -2); + lua_settable(L, -6); /* metatable[impname] = file */ + } + lua_settable(L, -3); /* io[name] = file */ +} + + +static int aux_close (lua_State *L) { + FILE *f = tofile(L, 1); + if (f == stdin || f == stdout || f == stderr) + return 0; /* file cannot be closed */ + else { + int ok = (pclose(f) != -1) || (fclose(f) == 0); + if (ok) + *(FILE **)lua_touserdata(L, 1) = NULL; /* mark file as closed */ + return ok; + } +} + + +static int io_close (lua_State *L) { + if (lua_isnone(L, 1)) { + lua_pushstring(L, IO_OUTPUT); + lua_rawget(L, lua_upvalueindex(1)); + } + return pushresult(L, aux_close(L), NULL); +} + + +static int io_gc (lua_State *L) { + FILE **f = topfile(L, 1); + if (*f != NULL) /* ignore closed files */ + aux_close(L); + return 0; +} + + +static int io_tostring (lua_State *L) { + char buff[32]; + FILE **f = topfile(L, 1); + if (*f == NULL) + strcpy(buff, "closed"); + else + sprintf(buff, "%p", lua_touserdata(L, 1)); + lua_pushfstring(L, "file (%s)", buff); + return 1; +} + + +static int io_open (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + FILE **pf = newfile(L); + *pf = fopen(filename, mode); + return (*pf == NULL) ? pushresult(L, 0, filename) : 1; +} + + +static int io_popen (lua_State *L) { +#if !USE_POPEN + luaL_error(L, "`popen' not supported"); + return 0; +#else + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + FILE **pf = newfile(L); + *pf = popen(filename, mode); + return (*pf == NULL) ? pushresult(L, 0, filename) : 1; +#endif +} + + +static int io_tmpfile (lua_State *L) { + FILE **pf = newfile(L); + *pf = tmpfile(); + return (*pf == NULL) ? pushresult(L, 0, NULL) : 1; +} + + +static FILE *getiofile (lua_State *L, const char *name) { + lua_pushstring(L, name); + lua_rawget(L, lua_upvalueindex(1)); + return tofile(L, -1); +} + + +static int g_iofile (lua_State *L, const char *name, const char *mode) { + if (!lua_isnoneornil(L, 1)) { + const char *filename = lua_tostring(L, 1); + lua_pushstring(L, name); + if (filename) { + FILE **pf = newfile(L); + *pf = fopen(filename, mode); + if (*pf == NULL) { + lua_pushfstring(L, "%s: %s", filename, strerror(errno)); + luaL_argerror(L, 1, lua_tostring(L, -1)); + } + } + else { + tofile(L, 1); /* check that it's a valid file handle */ + lua_pushvalue(L, 1); + } + lua_rawset(L, lua_upvalueindex(1)); + } + /* return current value */ + lua_pushstring(L, name); + lua_rawget(L, lua_upvalueindex(1)); + return 1; +} + + +static int io_input (lua_State *L) { + return g_iofile(L, IO_INPUT, "r"); +} + + +static int io_output (lua_State *L) { + return g_iofile(L, IO_OUTPUT, "w"); +} + + +static int io_readline (lua_State *L); + + +static void aux_lines (lua_State *L, int idx, int close) { + lua_pushliteral(L, FILEHANDLE); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushvalue(L, idx); + lua_pushboolean(L, close); /* close/not close file when finished */ + lua_pushcclosure(L, io_readline, 3); +} + + +static int f_lines (lua_State *L) { + tofile(L, 1); /* check that it's a valid file handle */ + aux_lines(L, 1, 0); + return 1; +} + + +static int io_lines (lua_State *L) { + if (lua_isnoneornil(L, 1)) { /* no arguments? */ + lua_pushstring(L, IO_INPUT); + lua_rawget(L, lua_upvalueindex(1)); /* will iterate over default input */ + return f_lines(L); + } + else { + const char *filename = luaL_checkstring(L, 1); + FILE **pf = newfile(L); + *pf = fopen(filename, "r"); + luaL_argcheck(L, *pf, 1, strerror(errno)); + aux_lines(L, lua_gettop(L), 1); + return 1; + } +} + + +/* +** {====================================================== +** READ +** ======================================================= +*/ + + +static int read_number (lua_State *L, FILE *f) { + lua_Number d; + if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { + lua_pushnumber(L, d); + return 1; + } + else return 0; /* read fails */ +} + + +static int test_eof (lua_State *L, FILE *f) { + int c = getc(f); + ungetc(c, f); + lua_pushlstring(L, NULL, 0); + return (c != EOF); +} + + +static int read_line (lua_State *L, FILE *f) { + luaL_Buffer b; + luaL_buffinit(L, &b); + for (;;) { + size_t l; + char *p = luaL_prepbuffer(&b); + if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ + luaL_pushresult(&b); /* close buffer */ + return (lua_strlen(L, -1) > 0); /* check whether read something */ + } + l = strlen(p); + if (p[l-1] != '\n') + luaL_addsize(&b, l); + else { + luaL_addsize(&b, l - 1); /* do not include `eol' */ + luaL_pushresult(&b); /* close buffer */ + return 1; /* read at least an `eol' */ + } + } +} + + +static int read_chars (lua_State *L, FILE *f, size_t n) { + size_t rlen; /* how much to read */ + size_t nr; /* number of chars actually read */ + luaL_Buffer b; + luaL_buffinit(L, &b); + rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ + do { + char *p = luaL_prepbuffer(&b); + if (rlen > n) rlen = n; /* cannot read more than asked */ + nr = fread(p, sizeof(char), rlen, f); + luaL_addsize(&b, nr); + n -= nr; /* still have to read `n' chars */ + } while (n > 0 && nr == rlen); /* until end of count or eof */ + luaL_pushresult(&b); /* close buffer */ + return (n == 0 || lua_strlen(L, -1) > 0); +} + + +static int g_read (lua_State *L, FILE *f, int first) { + int nargs = lua_gettop(L) - 1; + int success; + int n; + if (nargs == 0) { /* no arguments? */ + success = read_line(L, f); + n = first+1; /* to return 1 result */ + } + else { /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + success = 1; + for (n = first; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)lua_tonumber(L, n); + success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); + } + else { + const char *p = lua_tostring(L, n); + luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); + switch (p[1]) { + case 'n': /* number */ + success = read_number(L, f); + break; + case 'l': /* line */ + success = read_line(L, f); + break; + case 'a': /* file */ + read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */ + success = 1; /* always success */ + break; + case 'w': /* word */ + return luaL_error(L, "obsolete option `*w' to `read'"); + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (!success) { + lua_pop(L, 1); /* remove last result */ + lua_pushnil(L); /* push nil instead */ + } + return n - first; +} + + +static int io_read (lua_State *L) { + return g_read(L, getiofile(L, IO_INPUT), 1); +} + + +static int f_read (lua_State *L) { + return g_read(L, tofile(L, 1), 2); +} + + +static int io_readline (lua_State *L) { + FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(2)); + if (f == NULL) /* file is already closed? */ + luaL_error(L, "file is already closed"); + if (read_line(L, f)) return 1; + else { /* EOF */ + if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */ + lua_settop(L, 0); + lua_pushvalue(L, lua_upvalueindex(2)); + aux_close(L); /* close it */ + } + return 0; + } +} + +/* }====================================================== */ + + +static int g_write (lua_State *L, FILE *f, int arg) { + int nargs = lua_gettop(L) - 1; + int status = 1; + for (; nargs--; arg++) { + if (lua_type(L, arg) == LUA_TNUMBER) { + /* optimization: could be done exactly as for strings */ + status = status && + fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; + } + else { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + status = status && (fwrite(s, sizeof(char), l, f) == l); + } + } + return pushresult(L, status, NULL); +} + + +static int io_write (lua_State *L) { + return g_write(L, getiofile(L, IO_OUTPUT), 1); +} + + +static int f_write (lua_State *L) { + return g_write(L, tofile(L, 1), 2); +} + + +static int f_seek (lua_State *L) { + static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; + static const char *const modenames[] = {"set", "cur", "end", NULL}; + FILE *f = tofile(L, 1); + int op = luaL_findstring(luaL_optstring(L, 2, "cur"), modenames); + long offset = luaL_optlong(L, 3, 0); + luaL_argcheck(L, op != -1, 2, "invalid mode"); + op = fseek(f, offset, mode[op]); + if (op) + return pushresult(L, 0, NULL); /* error */ + else { + lua_pushnumber(L, ftell(f)); + return 1; + } +} + + +static int io_flush (lua_State *L) { + return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); +} + + +static int f_flush (lua_State *L) { + return pushresult(L, fflush(tofile(L, 1)) == 0, NULL); +} + + +static const luaL_reg iolib[] = { + {"input", io_input}, + {"output", io_output}, + {"lines", io_lines}, + {"close", io_close}, + {"flush", io_flush}, + {"open", io_open}, + {"popen", io_popen}, + {"read", io_read}, + {"tmpfile", io_tmpfile}, + {"type", io_type}, + {"write", io_write}, + {NULL, NULL} +}; + + +static const luaL_reg flib[] = { + {"flush", f_flush}, + {"read", f_read}, + {"lines", f_lines}, + {"seek", f_seek}, + {"write", f_write}, + {"close", io_close}, + {"__gc", io_gc}, + {"__tostring", io_tostring}, + {NULL, NULL} +}; + + +static void createmeta (lua_State *L) { + luaL_newmetatable(L, FILEHANDLE); /* create new metatable for file handles */ + /* file methods */ + lua_pushliteral(L, "__index"); + lua_pushvalue(L, -2); /* push metatable */ + lua_rawset(L, -3); /* metatable.__index = metatable */ + luaL_openlib(L, NULL, flib, 0); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Other O.S. Operations +** ======================================================= +*/ + +static int io_execute (lua_State *L) { + lua_pushnumber(L, system(luaL_checkstring(L, 1))); + return 1; +} + + +static int io_remove (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + return pushresult(L, remove(filename) == 0, filename); +} + + +static int io_rename (lua_State *L) { + const char *fromname = luaL_checkstring(L, 1); + const char *toname = luaL_checkstring(L, 2); + return pushresult(L, rename(fromname, toname) == 0, fromname); +} + + +static int io_tmpname (lua_State *L) { +#if !USE_TMPNAME + luaL_error(L, "`tmpname' not supported"); + return 0; +#else + char buff[L_tmpnam]; + if (tmpnam(buff) != buff) + return luaL_error(L, "unable to generate a unique filename in `tmpname'"); + lua_pushstring(L, buff); + return 1; +#endif +} + + +static int io_getenv (lua_State *L) { + lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ + return 1; +} + + +static int io_clock (lua_State *L) { + lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); + return 1; +} + + +/* +** {====================================================== +** Time/Date operations +** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, +** wday=%w+1, yday=%j, isdst=? } +** ======================================================= +*/ + +static void setfield (lua_State *L, const char *key, int value) { + lua_pushstring(L, key); + lua_pushnumber(L, value); + lua_rawset(L, -3); +} + +static void setboolfield (lua_State *L, const char *key, int value) { + lua_pushstring(L, key); + lua_pushboolean(L, value); + lua_rawset(L, -3); +} + +static int getboolfield (lua_State *L, const char *key) { + int res; + lua_pushstring(L, key); + lua_gettable(L, -2); + res = lua_toboolean(L, -1); + lua_pop(L, 1); + return res; +} + + +static int getfield (lua_State *L, const char *key, int d) { + int res; + lua_pushstring(L, key); + lua_gettable(L, -2); + if (lua_isnumber(L, -1)) + res = (int)(lua_tonumber(L, -1)); + else { + if (d == -2) + return luaL_error(L, "field `%s' missing in date table", key); + res = d; + } + lua_pop(L, 1); + return res; +} + + +static int io_date (lua_State *L) { + const char *s = luaL_optstring(L, 1, "%c"); + time_t t = (time_t)(luaL_optnumber(L, 2, -1)); + struct tm *stm; + if (t == (time_t)(-1)) /* no time given? */ + t = time(NULL); /* use current time */ + if (*s == '!') { /* UTC? */ + stm = gmtime(&t); + s++; /* skip `!' */ + } + else + stm = localtime(&t); + if (stm == NULL) /* invalid date? */ + lua_pushnil(L); + else if (strcmp(s, "*t") == 0) { + lua_newtable(L); + setfield(L, "sec", stm->tm_sec); + setfield(L, "min", stm->tm_min); + setfield(L, "hour", stm->tm_hour); + setfield(L, "day", stm->tm_mday); + setfield(L, "month", stm->tm_mon+1); + setfield(L, "year", stm->tm_year+1900); + setfield(L, "wday", stm->tm_wday+1); + setfield(L, "yday", stm->tm_yday+1); + setboolfield(L, "isdst", stm->tm_isdst); + } + else { + char b[256]; + if (strftime(b, sizeof(b), s, stm)) + lua_pushstring(L, b); + else + return luaL_error(L, "`date' format too long"); + } + return 1; +} + + +static int io_time (lua_State *L) { + if (lua_isnoneornil(L, 1)) /* called without args? */ + lua_pushnumber(L, time(NULL)); /* return current time */ + else { + time_t t; + struct tm ts; + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 1); /* make sure table is at the top */ + ts.tm_sec = getfield(L, "sec", 0); + ts.tm_min = getfield(L, "min", 0); + ts.tm_hour = getfield(L, "hour", 12); + ts.tm_mday = getfield(L, "day", -2); + ts.tm_mon = getfield(L, "month", -2) - 1; + ts.tm_year = getfield(L, "year", -2) - 1900; + ts.tm_isdst = getboolfield(L, "isdst"); + t = mktime(&ts); + if (t == (time_t)(-1)) + lua_pushnil(L); + else + lua_pushnumber(L, t); + } + return 1; +} + + +static int io_difftime (lua_State *L) { + lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), + (time_t)(luaL_optnumber(L, 2, 0)))); + return 1; +} + +/* }====================================================== */ + + +static int io_setloc (lua_State *L) { + static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, + LC_NUMERIC, LC_TIME}; + static const char *const catnames[] = {"all", "collate", "ctype", "monetary", + "numeric", "time", NULL}; + const char *l = lua_tostring(L, 1); + int op = luaL_findstring(luaL_optstring(L, 2, "all"), catnames); + luaL_argcheck(L, l || lua_isnoneornil(L, 1), 1, "string expected"); + luaL_argcheck(L, op != -1, 2, "invalid option"); + lua_pushstring(L, setlocale(cat[op], l)); + return 1; +} + + +static int io_exit (lua_State *L) { + exit(luaL_optint(L, 1, EXIT_SUCCESS)); + return 0; /* to avoid warnings */ +} + +static const luaL_reg syslib[] = { + {"clock", io_clock}, + {"date", io_date}, + {"difftime", io_difftime}, + {"execute", io_execute}, + {"exit", io_exit}, + {"getenv", io_getenv}, + {"remove", io_remove}, + {"rename", io_rename}, + {"setlocale", io_setloc}, + {"time", io_time}, + {"tmpname", io_tmpname}, + {NULL, NULL} +}; + +/* }====================================================== */ + + + +LUALIB_API int luaopen_io (lua_State *L) { + luaL_openlib(L, LUA_OSLIBNAME, syslib, 0); + createmeta(L); + lua_pushvalue(L, -1); + luaL_openlib(L, LUA_IOLIBNAME, iolib, 1); + /* put predefined file handles into `io' table */ + registerfile(L, stdin, "stdin", IO_INPUT); + registerfile(L, stdout, "stdout", IO_OUTPUT); + registerfile(L, stderr, "stderr", NULL); + return 1; +} + diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lmathlib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lmathlib.c index 266cc707f7..93eb3cba9c 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lmathlib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lmathlib.c @@ -1,256 +1,256 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include -#include - -#define lmathlib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - -#undef PI -#define PI (3.14159265358979323846) -#define RADIANS_PER_DEGREE (PI/180.0) - - - -/* -** If you want Lua to operate in degrees (instead of radians), -** define USE_DEGREES -*/ -#ifdef USE_DEGREES -#define FROMRAD(a) ((a)/RADIANS_PER_DEGREE) -#define TORAD(a) ((a)*RADIANS_PER_DEGREE) -#else -#define FROMRAD(a) (a) -#define TORAD(a) (a) -#endif - - -static int math_abs (lua_State *L) { - lua_pushnumber(L, fabs(luaL_checknumber(L, 1))); - return 1; -} - -static int math_sin (lua_State *L) { - lua_pushnumber(L, sin(TORAD(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_cos (lua_State *L) { - lua_pushnumber(L, cos(TORAD(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_tan (lua_State *L) { - lua_pushnumber(L, tan(TORAD(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_asin (lua_State *L) { - lua_pushnumber(L, FROMRAD(asin(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_acos (lua_State *L) { - lua_pushnumber(L, FROMRAD(acos(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_atan (lua_State *L) { - lua_pushnumber(L, FROMRAD(atan(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_atan2 (lua_State *L) { - lua_pushnumber(L, FROMRAD(atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2)))); - return 1; -} - -static int math_ceil (lua_State *L) { - lua_pushnumber(L, ceil(luaL_checknumber(L, 1))); - return 1; -} - -static int math_floor (lua_State *L) { - lua_pushnumber(L, floor(luaL_checknumber(L, 1))); - return 1; -} - -static int math_mod (lua_State *L) { - lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); - return 1; -} - -static int math_sqrt (lua_State *L) { - lua_pushnumber(L, sqrt(luaL_checknumber(L, 1))); - return 1; -} - -static int math_pow (lua_State *L) { - lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); - return 1; -} - -static int math_log (lua_State *L) { - lua_pushnumber(L, log(luaL_checknumber(L, 1))); - return 1; -} - -static int math_log10 (lua_State *L) { - lua_pushnumber(L, log10(luaL_checknumber(L, 1))); - return 1; -} - -static int math_exp (lua_State *L) { - lua_pushnumber(L, exp(luaL_checknumber(L, 1))); - return 1; -} - -static int math_deg (lua_State *L) { - lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE); - return 1; -} - -static int math_rad (lua_State *L) { - lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE); - return 1; -} - -static int math_frexp (lua_State *L) { - int e; - lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e)); - lua_pushnumber(L, e); - return 2; -} - -static int math_ldexp (lua_State *L) { - lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2))); - return 1; -} - - - -static int math_min (lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - lua_Number dmin = luaL_checknumber(L, 1); - int i; - for (i=2; i<=n; i++) { - lua_Number d = luaL_checknumber(L, i); - if (d < dmin) - dmin = d; - } - lua_pushnumber(L, dmin); - return 1; -} - - -static int math_max (lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - lua_Number dmax = luaL_checknumber(L, 1); - int i; - for (i=2; i<=n; i++) { - lua_Number d = luaL_checknumber(L, i); - if (d > dmax) - dmax = d; - } - lua_pushnumber(L, dmax); - return 1; -} - - -static int math_random (lua_State *L) { - /* the `%' avoids the (rare) case of r==1, and is needed also because on - some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ - lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX; - switch (lua_gettop(L)) { /* check number of arguments */ - case 0: { /* no arguments */ - lua_pushnumber(L, r); /* Number between 0 and 1 */ - break; - } - case 1: { /* only upper limit */ - int u = luaL_checkint(L, 1); - luaL_argcheck(L, 1<=u, 1, "interval is empty"); - lua_pushnumber(L, (int)floor(r*u)+1); /* int between 1 and `u' */ - break; - } - case 2: { /* lower and upper limits */ - int l = luaL_checkint(L, 1); - int u = luaL_checkint(L, 2); - luaL_argcheck(L, l<=u, 2, "interval is empty"); - lua_pushnumber(L, (int)floor(r*(u-l+1))+l); /* int between `l' and `u' */ - break; - } - default: return luaL_error(L, "wrong number of arguments"); - } - return 1; -} - - -static int math_randomseed (lua_State *L) { - srand(luaL_checkint(L, 1)); - return 0; -} - - -static const luaL_reg mathlib[] = { - {"abs", math_abs}, - {"sin", math_sin}, - {"cos", math_cos}, - {"tan", math_tan}, - {"asin", math_asin}, - {"acos", math_acos}, - {"atan", math_atan}, - {"atan2", math_atan2}, - {"ceil", math_ceil}, - {"floor", math_floor}, - {"mod", math_mod}, - {"frexp", math_frexp}, - {"ldexp", math_ldexp}, - {"sqrt", math_sqrt}, - {"min", math_min}, - {"max", math_max}, - {"log", math_log}, - {"log10", math_log10}, - {"exp", math_exp}, - {"deg", math_deg}, - {"pow", math_pow}, - {"rad", math_rad}, - {"random", math_random}, - {"randomseed", math_randomseed}, - {NULL, NULL} -}; - - -/* -** Open math library -*/ -LUALIB_API int luaopen_math (lua_State *L) { - luaL_openlib(L, LUA_MATHLIBNAME, mathlib, 0); - lua_pushliteral(L, "pi"); - lua_pushnumber(L, PI); - lua_settable(L, -3); - lua_pushliteral(L, "__pow"); - lua_pushcfunction(L, math_pow); - lua_settable(L, LUA_GLOBALSINDEX); - return 1; -} - +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include +#include + +#define lmathlib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#undef PI +#define PI (3.14159265358979323846) +#define RADIANS_PER_DEGREE (PI/180.0) + + + +/* +** If you want Lua to operate in degrees (instead of radians), +** define USE_DEGREES +*/ +#ifdef USE_DEGREES +#define FROMRAD(a) ((a)/RADIANS_PER_DEGREE) +#define TORAD(a) ((a)*RADIANS_PER_DEGREE) +#else +#define FROMRAD(a) (a) +#define TORAD(a) (a) +#endif + + +static int math_abs (lua_State *L) { + lua_pushnumber(L, fabs(luaL_checknumber(L, 1))); + return 1; +} + +static int math_sin (lua_State *L) { + lua_pushnumber(L, sin(TORAD(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_cos (lua_State *L) { + lua_pushnumber(L, cos(TORAD(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_tan (lua_State *L) { + lua_pushnumber(L, tan(TORAD(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_asin (lua_State *L) { + lua_pushnumber(L, FROMRAD(asin(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_acos (lua_State *L) { + lua_pushnumber(L, FROMRAD(acos(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_atan (lua_State *L) { + lua_pushnumber(L, FROMRAD(atan(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_atan2 (lua_State *L) { + lua_pushnumber(L, FROMRAD(atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2)))); + return 1; +} + +static int math_ceil (lua_State *L) { + lua_pushnumber(L, ceil(luaL_checknumber(L, 1))); + return 1; +} + +static int math_floor (lua_State *L) { + lua_pushnumber(L, floor(luaL_checknumber(L, 1))); + return 1; +} + +static int math_mod (lua_State *L) { + lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int math_sqrt (lua_State *L) { + lua_pushnumber(L, sqrt(luaL_checknumber(L, 1))); + return 1; +} + +static int math_pow (lua_State *L) { + lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int math_log (lua_State *L) { + lua_pushnumber(L, log(luaL_checknumber(L, 1))); + return 1; +} + +static int math_log10 (lua_State *L) { + lua_pushnumber(L, log10(luaL_checknumber(L, 1))); + return 1; +} + +static int math_exp (lua_State *L) { + lua_pushnumber(L, exp(luaL_checknumber(L, 1))); + return 1; +} + +static int math_deg (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE); + return 1; +} + +static int math_rad (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE); + return 1; +} + +static int math_frexp (lua_State *L) { + int e; + lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e)); + lua_pushnumber(L, e); + return 2; +} + +static int math_ldexp (lua_State *L) { + lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2))); + return 1; +} + + + +static int math_min (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + lua_Number dmin = luaL_checknumber(L, 1); + int i; + for (i=2; i<=n; i++) { + lua_Number d = luaL_checknumber(L, i); + if (d < dmin) + dmin = d; + } + lua_pushnumber(L, dmin); + return 1; +} + + +static int math_max (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + lua_Number dmax = luaL_checknumber(L, 1); + int i; + for (i=2; i<=n; i++) { + lua_Number d = luaL_checknumber(L, i); + if (d > dmax) + dmax = d; + } + lua_pushnumber(L, dmax); + return 1; +} + + +static int math_random (lua_State *L) { + /* the `%' avoids the (rare) case of r==1, and is needed also because on + some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ + lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX; + switch (lua_gettop(L)) { /* check number of arguments */ + case 0: { /* no arguments */ + lua_pushnumber(L, r); /* Number between 0 and 1 */ + break; + } + case 1: { /* only upper limit */ + int u = luaL_checkint(L, 1); + luaL_argcheck(L, 1<=u, 1, "interval is empty"); + lua_pushnumber(L, (int)floor(r*u)+1); /* int between 1 and `u' */ + break; + } + case 2: { /* lower and upper limits */ + int l = luaL_checkint(L, 1); + int u = luaL_checkint(L, 2); + luaL_argcheck(L, l<=u, 2, "interval is empty"); + lua_pushnumber(L, (int)floor(r*(u-l+1))+l); /* int between `l' and `u' */ + break; + } + default: return luaL_error(L, "wrong number of arguments"); + } + return 1; +} + + +static int math_randomseed (lua_State *L) { + srand(luaL_checkint(L, 1)); + return 0; +} + + +static const luaL_reg mathlib[] = { + {"abs", math_abs}, + {"sin", math_sin}, + {"cos", math_cos}, + {"tan", math_tan}, + {"asin", math_asin}, + {"acos", math_acos}, + {"atan", math_atan}, + {"atan2", math_atan2}, + {"ceil", math_ceil}, + {"floor", math_floor}, + {"mod", math_mod}, + {"frexp", math_frexp}, + {"ldexp", math_ldexp}, + {"sqrt", math_sqrt}, + {"min", math_min}, + {"max", math_max}, + {"log", math_log}, + {"log10", math_log10}, + {"exp", math_exp}, + {"deg", math_deg}, + {"pow", math_pow}, + {"rad", math_rad}, + {"random", math_random}, + {"randomseed", math_randomseed}, + {NULL, NULL} +}; + + +/* +** Open math library +*/ +LUALIB_API int luaopen_math (lua_State *L) { + luaL_openlib(L, LUA_MATHLIBNAME, mathlib, 0); + lua_pushliteral(L, "pi"); + lua_pushnumber(L, PI); + lua_settable(L, -3); + lua_pushliteral(L, "__pow"); + lua_pushcfunction(L, math_pow); + lua_settable(L, LUA_GLOBALSINDEX); + return 1; +} + diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/loadlib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/loadlib.c index e28d6018fb..6f4b8bb42d 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/loadlib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/loadlib.c @@ -1,205 +1,205 @@ -/* -** $Id: loadlib.c,v 1.1 2005/10/25 09:41:32 vizerie Exp $ -** Dynamic library loader for Lua -** See Copyright Notice in lua.h -* -* This Lua library exports a single function, called loadlib, which is -* called from Lua as loadlib(lib,init), where lib is the full name of the -* library to be loaded (including the complete path) and init is the name -* of a function to be called after the library is loaded. Typically, this -* function will register other functions, thus making the complete library -* available to Lua. The init function is *not* automatically called by -* loadlib. Instead, loadlib returns the init function as a Lua function -* that the client can call when it thinks is appropriate. In the case of -* errors, loadlib returns nil and two strings describing the error. -* The first string is supplied by the operating system; it should be -* informative and useful for error messages. The second string is "open", -* "init", or "absent" to identify the error and is meant to be used for -* making decisions without having to look into the first string (whose -* format is system-dependent). -* -* This module contains an implementation of loadlib for Unix systems that -* have dlfcn, an implementation for Windows, and a stub for other systems. -* See the list at the end of this file for some links to available -* implementations of dlfcn and interfaces to other native dynamic loaders -* on top of which loadlib could be implemented. -* -*/ - -#include "lua.h" -#include "lauxlib.h" -#include "lualib.h" - - -#undef LOADLIB - - -#ifdef USE_DLOPEN -#define LOADLIB -/* -* This is an implementation of loadlib based on the dlfcn interface. -* The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, -* NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least -* as an emulation layer on top of native functions. -*/ - -#include - -static int loadlib(lua_State *L) -{ - const char *path=luaL_checkstring(L,1); - const char *init=luaL_checkstring(L,2); - void *lib=dlopen(path,RTLD_NOW); - if (lib!=NULL) - { - lua_CFunction f=(lua_CFunction) dlsym(lib,init); - if (f!=NULL) - { - lua_pushlightuserdata(L,lib); - lua_pushcclosure(L,f,1); - return 1; - } - } - /* else return appropriate error messages */ - lua_pushnil(L); - lua_pushstring(L,dlerror()); - lua_pushstring(L,(lib!=NULL) ? "init" : "open"); - if (lib!=NULL) dlclose(lib); - return 3; -} - -#endif - - - -/* -** In Windows, default is to use dll; otherwise, default is not to use dll -*/ -#ifndef USE_DLL -#ifdef _WIN32 -#define USE_DLL 1 -#else -#define USE_DLL 0 -#endif -#endif - - -#if USE_DLL -#define LOADLIB -/* -* This is an implementation of loadlib for Windows using native functions. -*/ - -#include - -static void pusherror(lua_State *L) -{ - int error=GetLastError(); - char buffer[128]; - if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, - 0, error, 0, buffer, sizeof(buffer), 0)) - lua_pushstring(L,buffer); - else - lua_pushfstring(L,"system error %d\n",error); -} - -static int loadlib(lua_State *L) -{ - const char *path=luaL_checkstring(L,1); - const char *init=luaL_checkstring(L,2); - HINSTANCE lib=LoadLibrary(path); - if (lib!=NULL) - { - lua_CFunction f=(lua_CFunction) GetProcAddress(lib,init); - if (f!=NULL) - { - lua_pushlightuserdata(L,lib); - lua_pushcclosure(L,f,1); - return 1; - } - } - lua_pushnil(L); - pusherror(L); - lua_pushstring(L,(lib!=NULL) ? "init" : "open"); - if (lib!=NULL) FreeLibrary(lib); - return 3; -} - -#endif - - - -#ifndef LOADLIB -/* Fallback for other systems */ - -/* -** Those systems support dlopen, so they should have defined USE_DLOPEN. -** The default (no)implementation gives them a special error message. -*/ -#ifdef linux -#define LOADLIB -#endif - -#ifdef sun -#define LOADLIB -#endif - -#ifdef sgi -#define LOADLIB -#endif - -#ifdef BSD -#define LOADLIB -#endif - -#ifdef _WIN32 -#define LOADLIB -#endif - -#ifdef LOADLIB -#undef LOADLIB -#define LOADLIB "`loadlib' not installed (check your Lua configuration)" -#else -#define LOADLIB "`loadlib' not supported" -#endif - -static int loadlib(lua_State *L) -{ - lua_pushnil(L); - lua_pushliteral(L,LOADLIB); - lua_pushliteral(L,"absent"); - return 3; -} -#endif - -LUALIB_API int luaopen_loadlib (lua_State *L) -{ - lua_register(L,"loadlib",loadlib); - return 0; -} - -/* -* Here are some links to available implementations of dlfcn and -* interfaces to other native dynamic loaders on top of which loadlib -* could be implemented. Please send contributions and corrections to us. -* -* AIX -* Starting with AIX 4.2, dlfcn is included in the base OS. -* There is also an emulation package available. -* http://www.faqs.org/faqs/aix-faq/part4/section-21.html -* -* HPUX -* HPUX 11 has dlfcn. For HPUX 10 use shl_*. -* http://www.geda.seul.org/mailinglist/geda-dev37/msg00094.html -* http://www.stat.umn.edu/~luke/xls/projects/dlbasics/dlbasics.html -* -* Macintosh, Windows -* http://www.stat.umn.edu/~luke/xls/projects/dlbasics/dlbasics.html -* -* Mac OS X/Darwin -* http://www.opendarwin.org/projects/dlcompat/ -* -* GLIB has wrapper code for BeOS, OS2, Unix and Windows -* http://cvs.gnome.org/lxr/source/glib/gmodule/ -* -*/ +/* +** $Id: loadlib.c,v 1.1 2005/10/25 09:41:32 vizerie Exp $ +** Dynamic library loader for Lua +** See Copyright Notice in lua.h +* +* This Lua library exports a single function, called loadlib, which is +* called from Lua as loadlib(lib,init), where lib is the full name of the +* library to be loaded (including the complete path) and init is the name +* of a function to be called after the library is loaded. Typically, this +* function will register other functions, thus making the complete library +* available to Lua. The init function is *not* automatically called by +* loadlib. Instead, loadlib returns the init function as a Lua function +* that the client can call when it thinks is appropriate. In the case of +* errors, loadlib returns nil and two strings describing the error. +* The first string is supplied by the operating system; it should be +* informative and useful for error messages. The second string is "open", +* "init", or "absent" to identify the error and is meant to be used for +* making decisions without having to look into the first string (whose +* format is system-dependent). +* +* This module contains an implementation of loadlib for Unix systems that +* have dlfcn, an implementation for Windows, and a stub for other systems. +* See the list at the end of this file for some links to available +* implementations of dlfcn and interfaces to other native dynamic loaders +* on top of which loadlib could be implemented. +* +*/ + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + + +#undef LOADLIB + + +#ifdef USE_DLOPEN +#define LOADLIB +/* +* This is an implementation of loadlib based on the dlfcn interface. +* The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, +* NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least +* as an emulation layer on top of native functions. +*/ + +#include + +static int loadlib(lua_State *L) +{ + const char *path=luaL_checkstring(L,1); + const char *init=luaL_checkstring(L,2); + void *lib=dlopen(path,RTLD_NOW); + if (lib!=NULL) + { + lua_CFunction f=(lua_CFunction) dlsym(lib,init); + if (f!=NULL) + { + lua_pushlightuserdata(L,lib); + lua_pushcclosure(L,f,1); + return 1; + } + } + /* else return appropriate error messages */ + lua_pushnil(L); + lua_pushstring(L,dlerror()); + lua_pushstring(L,(lib!=NULL) ? "init" : "open"); + if (lib!=NULL) dlclose(lib); + return 3; +} + +#endif + + + +/* +** In Windows, default is to use dll; otherwise, default is not to use dll +*/ +#ifndef USE_DLL +#ifdef _WIN32 +#define USE_DLL 1 +#else +#define USE_DLL 0 +#endif +#endif + + +#if USE_DLL +#define LOADLIB +/* +* This is an implementation of loadlib for Windows using native functions. +*/ + +#include + +static void pusherror(lua_State *L) +{ + int error=GetLastError(); + char buffer[128]; + if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, + 0, error, 0, buffer, sizeof(buffer), 0)) + lua_pushstring(L,buffer); + else + lua_pushfstring(L,"system error %d\n",error); +} + +static int loadlib(lua_State *L) +{ + const char *path=luaL_checkstring(L,1); + const char *init=luaL_checkstring(L,2); + HINSTANCE lib=LoadLibrary(path); + if (lib!=NULL) + { + lua_CFunction f=(lua_CFunction) GetProcAddress(lib,init); + if (f!=NULL) + { + lua_pushlightuserdata(L,lib); + lua_pushcclosure(L,f,1); + return 1; + } + } + lua_pushnil(L); + pusherror(L); + lua_pushstring(L,(lib!=NULL) ? "init" : "open"); + if (lib!=NULL) FreeLibrary(lib); + return 3; +} + +#endif + + + +#ifndef LOADLIB +/* Fallback for other systems */ + +/* +** Those systems support dlopen, so they should have defined USE_DLOPEN. +** The default (no)implementation gives them a special error message. +*/ +#ifdef linux +#define LOADLIB +#endif + +#ifdef sun +#define LOADLIB +#endif + +#ifdef sgi +#define LOADLIB +#endif + +#ifdef BSD +#define LOADLIB +#endif + +#ifdef _WIN32 +#define LOADLIB +#endif + +#ifdef LOADLIB +#undef LOADLIB +#define LOADLIB "`loadlib' not installed (check your Lua configuration)" +#else +#define LOADLIB "`loadlib' not supported" +#endif + +static int loadlib(lua_State *L) +{ + lua_pushnil(L); + lua_pushliteral(L,LOADLIB); + lua_pushliteral(L,"absent"); + return 3; +} +#endif + +LUALIB_API int luaopen_loadlib (lua_State *L) +{ + lua_register(L,"loadlib",loadlib); + return 0; +} + +/* +* Here are some links to available implementations of dlfcn and +* interfaces to other native dynamic loaders on top of which loadlib +* could be implemented. Please send contributions and corrections to us. +* +* AIX +* Starting with AIX 4.2, dlfcn is included in the base OS. +* There is also an emulation package available. +* http://www.faqs.org/faqs/aix-faq/part4/section-21.html +* +* HPUX +* HPUX 11 has dlfcn. For HPUX 10 use shl_*. +* http://www.geda.seul.org/mailinglist/geda-dev37/msg00094.html +* http://www.stat.umn.edu/~luke/xls/projects/dlbasics/dlbasics.html +* +* Macintosh, Windows +* http://www.stat.umn.edu/~luke/xls/projects/dlbasics/dlbasics.html +* +* Mac OS X/Darwin +* http://www.opendarwin.org/projects/dlcompat/ +* +* GLIB has wrapper code for BeOS, OS2, Unix and Windows +* http://cvs.gnome.org/lxr/source/glib/gmodule/ +* +*/ diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lstrlib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lstrlib.c index 28cbe0bf42..69e40064b9 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lstrlib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lstrlib.c @@ -1,780 +1,780 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include -#include -#include -#include -#include - -#define lstrlib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - -/* macro to `unsign' a character */ -#ifndef uchar -#define uchar(c) ((unsigned char)(c)) -#endif - - -typedef long sint32; /* a signed version for size_t */ - - -static int str_len (lua_State *L) { - size_t l; - luaL_checklstring(L, 1, &l); - lua_pushnumber(L, (lua_Number)l); - return 1; -} - - -static sint32 posrelat (sint32 pos, size_t len) { - /* relative string position: negative means back from end */ - return (pos>=0) ? pos : (sint32)len+pos+1; -} - - -static int str_sub (lua_State *L) { - size_t l; - const char *s = luaL_checklstring(L, 1, &l); - sint32 start = posrelat(luaL_checklong(L, 2), l); - sint32 end = posrelat(luaL_optlong(L, 3, -1), l); - if (start < 1) start = 1; - if (end > (sint32)l) end = (sint32)l; - if (start <= end) - lua_pushlstring(L, s+start-1, end-start+1); - else lua_pushliteral(L, ""); - return 1; -} - - -static int str_lower (lua_State *L) { - size_t l; - size_t i; - luaL_Buffer b; - const char *s = luaL_checklstring(L, 1, &l); - luaL_buffinit(L, &b); - for (i=0; i 0) - luaL_addlstring(&b, s, l); - luaL_pushresult(&b); - return 1; -} - - -static int str_byte (lua_State *L) { - size_t l; - const char *s = luaL_checklstring(L, 1, &l); - sint32 pos = posrelat(luaL_optlong(L, 2, 1), l); - if (pos <= 0 || (size_t)(pos) > l) /* index out of range? */ - return 0; /* no answer */ - lua_pushnumber(L, uchar(s[pos-1])); - return 1; -} - - -static int str_char (lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - int i; - luaL_Buffer b; - luaL_buffinit(L, &b); - for (i=1; i<=n; i++) { - int c = luaL_checkint(L, i); - luaL_argcheck(L, uchar(c) == c, i, "invalid value"); - luaL_putchar(&b, uchar(c)); - } - luaL_pushresult(&b); - return 1; -} - - -static int writer (lua_State *L, const void* b, size_t size, void* B) { - (void)L; - luaL_addlstring((luaL_Buffer*) B, (const char *)b, size); - return 1; -} - - -static int str_dump (lua_State *L) { - luaL_Buffer b; - luaL_checktype(L, 1, LUA_TFUNCTION); - luaL_buffinit(L,&b); - if (!lua_dump(L, writer, &b)) - luaL_error(L, "unable to dump given function"); - luaL_pushresult(&b); - return 1; -} - - - -/* -** {====================================================== -** PATTERN MATCHING -** ======================================================= -*/ - -#ifndef MAX_CAPTURES -#define MAX_CAPTURES 32 /* arbitrary limit */ -#endif - - -#define CAP_UNFINISHED (-1) -#define CAP_POSITION (-2) - -typedef struct MatchState { - const char *src_init; /* init of source string */ - const char *src_end; /* end (`\0') of source string */ - lua_State *L; - int level; /* total number of captures (finished or unfinished) */ - struct { - const char *init; - sint32 len; - } capture[MAX_CAPTURES]; -} MatchState; - - -#define ESC '%' -#define SPECIALS "^$*+?.([%-" - - -static int check_capture (MatchState *ms, int l) { - l -= '1'; - if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) - return luaL_error(ms->L, "invalid capture index"); - return l; -} - - -static int capture_to_close (MatchState *ms) { - int level = ms->level; - for (level--; level>=0; level--) - if (ms->capture[level].len == CAP_UNFINISHED) return level; - return luaL_error(ms->L, "invalid pattern capture"); -} - - -static const char *luaI_classend (MatchState *ms, const char *p) { - switch (*p++) { - case ESC: { - if (*p == '\0') - luaL_error(ms->L, "malformed pattern (ends with `%')"); - return p+1; - } - case '[': { - if (*p == '^') p++; - do { /* look for a `]' */ - if (*p == '\0') - luaL_error(ms->L, "malformed pattern (missing `]')"); - if (*(p++) == ESC && *p != '\0') - p++; /* skip escapes (e.g. `%]') */ - } while (*p != ']'); - return p+1; - } - default: { - return p; - } - } -} - - -static int match_class (int c, int cl) { - int res; - switch (tolower(cl)) { - case 'a' : res = isalpha(c); break; - case 'c' : res = iscntrl(c); break; - case 'd' : res = isdigit(c); break; - case 'l' : res = islower(c); break; - case 'p' : res = ispunct(c); break; - case 's' : res = isspace(c); break; - case 'u' : res = isupper(c); break; - case 'w' : res = isalnum(c); break; - case 'x' : res = isxdigit(c); break; - case 'z' : res = (c == 0); break; - default: return (cl == c); - } - return (islower(cl) ? res : !res); -} - - -static int matchbracketclass (int c, const char *p, const char *ec) { - int sig = 1; - if (*(p+1) == '^') { - sig = 0; - p++; /* skip the `^' */ - } - while (++p < ec) { - if (*p == ESC) { - p++; - if (match_class(c, *p)) - return sig; - } - else if ((*(p+1) == '-') && (p+2 < ec)) { - p+=2; - if (uchar(*(p-2)) <= c && c <= uchar(*p)) - return sig; - } - else if (uchar(*p) == c) return sig; - } - return !sig; -} - - -static int luaI_singlematch (int c, const char *p, const char *ep) { - switch (*p) { - case '.': return 1; /* matches any char */ - case ESC: return match_class(c, *(p+1)); - case '[': return matchbracketclass(c, p, ep-1); - default: return (uchar(*p) == c); - } -} - - -static const char *match (MatchState *ms, const char *s, const char *p); - - -static const char *matchbalance (MatchState *ms, const char *s, - const char *p) { - if (*p == 0 || *(p+1) == 0) - luaL_error(ms->L, "unbalanced pattern"); - if (*s != *p) return NULL; - else { - int b = *p; - int e = *(p+1); - int cont = 1; - while (++s < ms->src_end) { - if (*s == e) { - if (--cont == 0) return s+1; - } - else if (*s == b) cont++; - } - } - return NULL; /* string ends out of balance */ -} - - -static const char *max_expand (MatchState *ms, const char *s, - const char *p, const char *ep) { - sint32 i = 0; /* counts maximum expand for item */ - while ((s+i)src_end && luaI_singlematch(uchar(*(s+i)), p, ep)) - i++; - /* keeps trying to match with the maximum repetitions */ - while (i>=0) { - const char *res = match(ms, (s+i), ep+1); - if (res) return res; - i--; /* else didn't match; reduce 1 repetition to try again */ - } - return NULL; -} - - -static const char *min_expand (MatchState *ms, const char *s, - const char *p, const char *ep) { - for (;;) { - const char *res = match(ms, s, ep+1); - if (res != NULL) - return res; - else if (ssrc_end && luaI_singlematch(uchar(*s), p, ep)) - s++; /* try with one more repetition */ - else return NULL; - } -} - - -static const char *start_capture (MatchState *ms, const char *s, - const char *p, int what) { - const char *res; - int level = ms->level; - if (level >= MAX_CAPTURES) luaL_error(ms->L, "too many captures"); - ms->capture[level].init = s; - ms->capture[level].len = what; - ms->level = level+1; - if ((res=match(ms, s, p)) == NULL) /* match failed? */ - ms->level--; /* undo capture */ - return res; -} - - -static const char *end_capture (MatchState *ms, const char *s, - const char *p) { - int l = capture_to_close(ms); - const char *res; - ms->capture[l].len = s - ms->capture[l].init; /* close capture */ - if ((res = match(ms, s, p)) == NULL) /* match failed? */ - ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ - return res; -} - - -static const char *match_capture (MatchState *ms, const char *s, int l) { - size_t len; - l = check_capture(ms, l); - len = ms->capture[l].len; - if ((size_t)(ms->src_end-s) >= len && - memcmp(ms->capture[l].init, s, len) == 0) - return s+len; - else return NULL; -} - - -static const char *match (MatchState *ms, const char *s, const char *p) { - init: /* using goto's to optimize tail recursion */ - switch (*p) { - case '(': { /* start capture */ - if (*(p+1) == ')') /* position capture? */ - return start_capture(ms, s, p+2, CAP_POSITION); - else - return start_capture(ms, s, p+1, CAP_UNFINISHED); - } - case ')': { /* end capture */ - return end_capture(ms, s, p+1); - } - case ESC: { - switch (*(p+1)) { - case 'b': { /* balanced string? */ - s = matchbalance(ms, s, p+2); - if (s == NULL) return NULL; - p+=4; goto init; /* else return match(ms, s, p+4); */ - } - case 'f': { /* frontier? */ - const char *ep; char previous; - p += 2; - if (*p != '[') - luaL_error(ms->L, "missing `[' after `%%f' in pattern"); - ep = luaI_classend(ms, p); /* points to what is next */ - previous = (s == ms->src_init) ? '\0' : *(s-1); - if (matchbracketclass(uchar(previous), p, ep-1) || - !matchbracketclass(uchar(*s), p, ep-1)) return NULL; - p=ep; goto init; /* else return match(ms, s, ep); */ - } - default: { - if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */ - s = match_capture(ms, s, *(p+1)); - if (s == NULL) return NULL; - p+=2; goto init; /* else return match(ms, s, p+2) */ - } - goto dflt; /* case default */ - } - } - } - case '\0': { /* end of pattern */ - return s; /* match succeeded */ - } - case '$': { - if (*(p+1) == '\0') /* is the `$' the last char in pattern? */ - return (s == ms->src_end) ? s : NULL; /* check end of string */ - else goto dflt; - } - default: dflt: { /* it is a pattern item */ - const char *ep = luaI_classend(ms, p); /* points to what is next */ - int m = ssrc_end && luaI_singlematch(uchar(*s), p, ep); - switch (*ep) { - case '?': { /* optional */ - const char *res; - if (m && ((res=match(ms, s+1, ep+1)) != NULL)) - return res; - p=ep+1; goto init; /* else return match(ms, s, ep+1); */ - } - case '*': { /* 0 or more repetitions */ - return max_expand(ms, s, p, ep); - } - case '+': { /* 1 or more repetitions */ - return (m ? max_expand(ms, s+1, p, ep) : NULL); - } - case '-': { /* 0 or more repetitions (minimum) */ - return min_expand(ms, s, p, ep); - } - default: { - if (!m) return NULL; - s++; p=ep; goto init; /* else return match(ms, s+1, ep); */ - } - } - } - } -} - - - -static const char *lmemfind (const char *s1, size_t l1, - const char *s2, size_t l2) { - if (l2 == 0) return s1; /* empty strings are everywhere */ - else if (l2 > l1) return NULL; /* avoids a negative `l1' */ - else { - const char *init; /* to search for a `*s2' inside `s1' */ - l2--; /* 1st char will be checked by `memchr' */ - l1 = l1-l2; /* `s2' cannot be found after that */ - while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { - init++; /* 1st char is already checked */ - if (memcmp(init, s2+1, l2) == 0) - return init-1; - else { /* correct `l1' and `s1' to try again */ - l1 -= init-s1; - s1 = init; - } - } - return NULL; /* not found */ - } -} - - -static void push_onecapture (MatchState *ms, int i) { - int l = ms->capture[i].len; - if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); - if (l == CAP_POSITION) - lua_pushnumber(ms->L, (lua_Number)(ms->capture[i].init - ms->src_init + 1)); - else - lua_pushlstring(ms->L, ms->capture[i].init, l); -} - - -static int push_captures (MatchState *ms, const char *s, const char *e) { - int i; - luaL_checkstack(ms->L, ms->level, "too many captures"); - if (ms->level == 0 && s) { /* no explicit captures? */ - lua_pushlstring(ms->L, s, e-s); /* return whole match */ - return 1; - } - else { /* return all captures */ - for (i=0; ilevel; i++) - push_onecapture(ms, i); - return ms->level; /* number of strings pushed */ - } -} - - -static int str_find (lua_State *L) { - size_t l1, l2; - const char *s = luaL_checklstring(L, 1, &l1); - const char *p = luaL_checklstring(L, 2, &l2); - sint32 init = posrelat(luaL_optlong(L, 3, 1), l1) - 1; - if (init < 0) init = 0; - else if ((size_t)(init) > l1) init = (sint32)l1; - if (lua_toboolean(L, 4) || /* explicit request? */ - strpbrk(p, SPECIALS) == NULL) { /* or no special characters? */ - /* do a plain search */ - const char *s2 = lmemfind(s+init, l1-init, p, l2); - if (s2) { - lua_pushnumber(L, (lua_Number)(s2-s+1)); - lua_pushnumber(L, (lua_Number)(s2-s+l2)); - return 2; - } - } - else { - MatchState ms; - int anchor = (*p == '^') ? (p++, 1) : 0; - const char *s1=s+init; - ms.L = L; - ms.src_init = s; - ms.src_end = s+l1; - do { - const char *res; - ms.level = 0; - if ((res=match(&ms, s1, p)) != NULL) { - lua_pushnumber(L, (lua_Number)(s1-s+1)); /* start */ - lua_pushnumber(L, (lua_Number)(res-s)); /* end */ - return push_captures(&ms, NULL, 0) + 2; - } - } while (s1++L; - if (lua_isstring(L, 3)) { - const char *news = lua_tostring(L, 3); - size_t l = lua_strlen(L, 3); - size_t i; - for (i=0; i= 3 && (lua_isstring(L, 3) || lua_isfunction(L, 3)), - 3, "string or function expected"); - luaL_buffinit(L, &b); - ms.L = L; - ms.src_init = src; - ms.src_end = src+srcl; - while (n < max_s) { - const char *e; - ms.level = 0; - e = match(&ms, src, p); - if (e) { - n++; - add_s(&ms, &b, src, e); - } - if (e && e>src) /* non empty match? */ - src = e; /* skip it */ - else if (src < ms.src_end) - luaL_putchar(&b, *src++); - else break; - if (anchor) break; - } - luaL_addlstring(&b, src, ms.src_end-src); - luaL_pushresult(&b); - lua_pushnumber(L, (lua_Number)n); /* number of substitutions */ - return 2; -} - -/* }====================================================== */ - - -/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ -#define MAX_ITEM 512 -/* maximum size of each format specification (such as '%-099.99d') */ -#define MAX_FORMAT 20 - - -static void luaI_addquoted (lua_State *L, luaL_Buffer *b, int arg) { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); - luaL_putchar(b, '"'); - while (l--) { - switch (*s) { - case '"': case '\\': case '\n': { - luaL_putchar(b, '\\'); - luaL_putchar(b, *s); - break; - } - case '\0': { - luaL_addlstring(b, "\\000", 4); - break; - } - default: { - luaL_putchar(b, *s); - break; - } - } - s++; - } - luaL_putchar(b, '"'); -} - - -static const char *scanformat (lua_State *L, const char *strfrmt, - char *form, int *hasprecision) { - const char *p = strfrmt; - while (strchr("-+ #0", *p)) p++; /* skip flags */ - if (isdigit(uchar(*p))) p++; /* skip width */ - if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ - if (*p == '.') { - p++; - *hasprecision = 1; - if (isdigit(uchar(*p))) p++; /* skip precision */ - if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ - } - if (isdigit(uchar(*p))) - luaL_error(L, "invalid format (width or precision too long)"); - if (p-strfrmt+2 > MAX_FORMAT) /* +2 to include `%' and the specifier */ - luaL_error(L, "invalid format (too long)"); - form[0] = '%'; - strncpy(form+1, strfrmt, p-strfrmt+1); - form[p-strfrmt+2] = 0; - return p; -} - - -static int str_format (lua_State *L) { - int arg = 1; - size_t sfl; - const char *strfrmt = luaL_checklstring(L, arg, &sfl); - const char *strfrmt_end = strfrmt+sfl; - luaL_Buffer b; - luaL_buffinit(L, &b); - while (strfrmt < strfrmt_end) { - if (*strfrmt != '%') - luaL_putchar(&b, *strfrmt++); - else if (*++strfrmt == '%') - luaL_putchar(&b, *strfrmt++); /* %% */ - else { /* format item */ - char form[MAX_FORMAT]; /* to store the format (`%...') */ - char buff[MAX_ITEM]; /* to store the formatted item */ - int hasprecision = 0; - if (isdigit(uchar(*strfrmt)) && *(strfrmt+1) == '$') - return luaL_error(L, "obsolete option (d$) to `format'"); - arg++; - strfrmt = scanformat(L, strfrmt, form, &hasprecision); - switch (*strfrmt++) { - case 'c': case 'd': case 'i': { - sprintf(buff, form, luaL_checkint(L, arg)); - break; - } - case 'o': case 'u': case 'x': case 'X': { - sprintf(buff, form, (unsigned int)(luaL_checknumber(L, arg))); - break; - } - case 'e': case 'E': case 'f': - case 'g': case 'G': { - sprintf(buff, form, luaL_checknumber(L, arg)); - break; - } - case 'q': { - luaI_addquoted(L, &b, arg); - continue; /* skip the `addsize' at the end */ - } - case 's': { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); - if (!hasprecision && l >= 100) { - /* no precision and string is too long to be formatted; - keep original string */ - lua_pushvalue(L, arg); - luaL_addvalue(&b); - continue; /* skip the `addsize' at the end */ - } - else { - sprintf(buff, form, s); - break; - } - } - default: { /* also treat cases `pnLlh' */ - return luaL_error(L, "invalid option to `format'"); - } - } - luaL_addlstring(&b, buff, strlen(buff)); - } - } - luaL_pushresult(&b); - return 1; -} - - -static const luaL_reg strlib[] = { - {"len", str_len}, - {"sub", str_sub}, - {"lower", str_lower}, - {"upper", str_upper}, - {"char", str_char}, - {"rep", str_rep}, - {"byte", str_byte}, - {"format", str_format}, - {"dump", str_dump}, - {"find", str_find}, - {"gfind", gfind}, - {"gsub", str_gsub}, - {NULL, NULL} -}; - - -/* -** Open string library -*/ -LUALIB_API int luaopen_string (lua_State *L) { - luaL_openlib(L, LUA_STRLIBNAME, strlib, 0); - return 1; -} - +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include +#include +#include +#include +#include + +#define lstrlib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* macro to `unsign' a character */ +#ifndef uchar +#define uchar(c) ((unsigned char)(c)) +#endif + + +typedef long sint32; /* a signed version for size_t */ + + +static int str_len (lua_State *L) { + size_t l; + luaL_checklstring(L, 1, &l); + lua_pushnumber(L, (lua_Number)l); + return 1; +} + + +static sint32 posrelat (sint32 pos, size_t len) { + /* relative string position: negative means back from end */ + return (pos>=0) ? pos : (sint32)len+pos+1; +} + + +static int str_sub (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + sint32 start = posrelat(luaL_checklong(L, 2), l); + sint32 end = posrelat(luaL_optlong(L, 3, -1), l); + if (start < 1) start = 1; + if (end > (sint32)l) end = (sint32)l; + if (start <= end) + lua_pushlstring(L, s+start-1, end-start+1); + else lua_pushliteral(L, ""); + return 1; +} + + +static int str_lower (lua_State *L) { + size_t l; + size_t i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + luaL_buffinit(L, &b); + for (i=0; i 0) + luaL_addlstring(&b, s, l); + luaL_pushresult(&b); + return 1; +} + + +static int str_byte (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + sint32 pos = posrelat(luaL_optlong(L, 2, 1), l); + if (pos <= 0 || (size_t)(pos) > l) /* index out of range? */ + return 0; /* no answer */ + lua_pushnumber(L, uchar(s[pos-1])); + return 1; +} + + +static int str_char (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + luaL_Buffer b; + luaL_buffinit(L, &b); + for (i=1; i<=n; i++) { + int c = luaL_checkint(L, i); + luaL_argcheck(L, uchar(c) == c, i, "invalid value"); + luaL_putchar(&b, uchar(c)); + } + luaL_pushresult(&b); + return 1; +} + + +static int writer (lua_State *L, const void* b, size_t size, void* B) { + (void)L; + luaL_addlstring((luaL_Buffer*) B, (const char *)b, size); + return 1; +} + + +static int str_dump (lua_State *L) { + luaL_Buffer b; + luaL_checktype(L, 1, LUA_TFUNCTION); + luaL_buffinit(L,&b); + if (!lua_dump(L, writer, &b)) + luaL_error(L, "unable to dump given function"); + luaL_pushresult(&b); + return 1; +} + + + +/* +** {====================================================== +** PATTERN MATCHING +** ======================================================= +*/ + +#ifndef MAX_CAPTURES +#define MAX_CAPTURES 32 /* arbitrary limit */ +#endif + + +#define CAP_UNFINISHED (-1) +#define CAP_POSITION (-2) + +typedef struct MatchState { + const char *src_init; /* init of source string */ + const char *src_end; /* end (`\0') of source string */ + lua_State *L; + int level; /* total number of captures (finished or unfinished) */ + struct { + const char *init; + sint32 len; + } capture[MAX_CAPTURES]; +} MatchState; + + +#define ESC '%' +#define SPECIALS "^$*+?.([%-" + + +static int check_capture (MatchState *ms, int l) { + l -= '1'; + if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) + return luaL_error(ms->L, "invalid capture index"); + return l; +} + + +static int capture_to_close (MatchState *ms) { + int level = ms->level; + for (level--; level>=0; level--) + if (ms->capture[level].len == CAP_UNFINISHED) return level; + return luaL_error(ms->L, "invalid pattern capture"); +} + + +static const char *luaI_classend (MatchState *ms, const char *p) { + switch (*p++) { + case ESC: { + if (*p == '\0') + luaL_error(ms->L, "malformed pattern (ends with `%')"); + return p+1; + } + case '[': { + if (*p == '^') p++; + do { /* look for a `]' */ + if (*p == '\0') + luaL_error(ms->L, "malformed pattern (missing `]')"); + if (*(p++) == ESC && *p != '\0') + p++; /* skip escapes (e.g. `%]') */ + } while (*p != ']'); + return p+1; + } + default: { + return p; + } + } +} + + +static int match_class (int c, int cl) { + int res; + switch (tolower(cl)) { + case 'a' : res = isalpha(c); break; + case 'c' : res = iscntrl(c); break; + case 'd' : res = isdigit(c); break; + case 'l' : res = islower(c); break; + case 'p' : res = ispunct(c); break; + case 's' : res = isspace(c); break; + case 'u' : res = isupper(c); break; + case 'w' : res = isalnum(c); break; + case 'x' : res = isxdigit(c); break; + case 'z' : res = (c == 0); break; + default: return (cl == c); + } + return (islower(cl) ? res : !res); +} + + +static int matchbracketclass (int c, const char *p, const char *ec) { + int sig = 1; + if (*(p+1) == '^') { + sig = 0; + p++; /* skip the `^' */ + } + while (++p < ec) { + if (*p == ESC) { + p++; + if (match_class(c, *p)) + return sig; + } + else if ((*(p+1) == '-') && (p+2 < ec)) { + p+=2; + if (uchar(*(p-2)) <= c && c <= uchar(*p)) + return sig; + } + else if (uchar(*p) == c) return sig; + } + return !sig; +} + + +static int luaI_singlematch (int c, const char *p, const char *ep) { + switch (*p) { + case '.': return 1; /* matches any char */ + case ESC: return match_class(c, *(p+1)); + case '[': return matchbracketclass(c, p, ep-1); + default: return (uchar(*p) == c); + } +} + + +static const char *match (MatchState *ms, const char *s, const char *p); + + +static const char *matchbalance (MatchState *ms, const char *s, + const char *p) { + if (*p == 0 || *(p+1) == 0) + luaL_error(ms->L, "unbalanced pattern"); + if (*s != *p) return NULL; + else { + int b = *p; + int e = *(p+1); + int cont = 1; + while (++s < ms->src_end) { + if (*s == e) { + if (--cont == 0) return s+1; + } + else if (*s == b) cont++; + } + } + return NULL; /* string ends out of balance */ +} + + +static const char *max_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + sint32 i = 0; /* counts maximum expand for item */ + while ((s+i)src_end && luaI_singlematch(uchar(*(s+i)), p, ep)) + i++; + /* keeps trying to match with the maximum repetitions */ + while (i>=0) { + const char *res = match(ms, (s+i), ep+1); + if (res) return res; + i--; /* else didn't match; reduce 1 repetition to try again */ + } + return NULL; +} + + +static const char *min_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + for (;;) { + const char *res = match(ms, s, ep+1); + if (res != NULL) + return res; + else if (ssrc_end && luaI_singlematch(uchar(*s), p, ep)) + s++; /* try with one more repetition */ + else return NULL; + } +} + + +static const char *start_capture (MatchState *ms, const char *s, + const char *p, int what) { + const char *res; + int level = ms->level; + if (level >= MAX_CAPTURES) luaL_error(ms->L, "too many captures"); + ms->capture[level].init = s; + ms->capture[level].len = what; + ms->level = level+1; + if ((res=match(ms, s, p)) == NULL) /* match failed? */ + ms->level--; /* undo capture */ + return res; +} + + +static const char *end_capture (MatchState *ms, const char *s, + const char *p) { + int l = capture_to_close(ms); + const char *res; + ms->capture[l].len = s - ms->capture[l].init; /* close capture */ + if ((res = match(ms, s, p)) == NULL) /* match failed? */ + ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ + return res; +} + + +static const char *match_capture (MatchState *ms, const char *s, int l) { + size_t len; + l = check_capture(ms, l); + len = ms->capture[l].len; + if ((size_t)(ms->src_end-s) >= len && + memcmp(ms->capture[l].init, s, len) == 0) + return s+len; + else return NULL; +} + + +static const char *match (MatchState *ms, const char *s, const char *p) { + init: /* using goto's to optimize tail recursion */ + switch (*p) { + case '(': { /* start capture */ + if (*(p+1) == ')') /* position capture? */ + return start_capture(ms, s, p+2, CAP_POSITION); + else + return start_capture(ms, s, p+1, CAP_UNFINISHED); + } + case ')': { /* end capture */ + return end_capture(ms, s, p+1); + } + case ESC: { + switch (*(p+1)) { + case 'b': { /* balanced string? */ + s = matchbalance(ms, s, p+2); + if (s == NULL) return NULL; + p+=4; goto init; /* else return match(ms, s, p+4); */ + } + case 'f': { /* frontier? */ + const char *ep; char previous; + p += 2; + if (*p != '[') + luaL_error(ms->L, "missing `[' after `%%f' in pattern"); + ep = luaI_classend(ms, p); /* points to what is next */ + previous = (s == ms->src_init) ? '\0' : *(s-1); + if (matchbracketclass(uchar(previous), p, ep-1) || + !matchbracketclass(uchar(*s), p, ep-1)) return NULL; + p=ep; goto init; /* else return match(ms, s, ep); */ + } + default: { + if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */ + s = match_capture(ms, s, *(p+1)); + if (s == NULL) return NULL; + p+=2; goto init; /* else return match(ms, s, p+2) */ + } + goto dflt; /* case default */ + } + } + } + case '\0': { /* end of pattern */ + return s; /* match succeeded */ + } + case '$': { + if (*(p+1) == '\0') /* is the `$' the last char in pattern? */ + return (s == ms->src_end) ? s : NULL; /* check end of string */ + else goto dflt; + } + default: dflt: { /* it is a pattern item */ + const char *ep = luaI_classend(ms, p); /* points to what is next */ + int m = ssrc_end && luaI_singlematch(uchar(*s), p, ep); + switch (*ep) { + case '?': { /* optional */ + const char *res; + if (m && ((res=match(ms, s+1, ep+1)) != NULL)) + return res; + p=ep+1; goto init; /* else return match(ms, s, ep+1); */ + } + case '*': { /* 0 or more repetitions */ + return max_expand(ms, s, p, ep); + } + case '+': { /* 1 or more repetitions */ + return (m ? max_expand(ms, s+1, p, ep) : NULL); + } + case '-': { /* 0 or more repetitions (minimum) */ + return min_expand(ms, s, p, ep); + } + default: { + if (!m) return NULL; + s++; p=ep; goto init; /* else return match(ms, s+1, ep); */ + } + } + } + } +} + + + +static const char *lmemfind (const char *s1, size_t l1, + const char *s2, size_t l2) { + if (l2 == 0) return s1; /* empty strings are everywhere */ + else if (l2 > l1) return NULL; /* avoids a negative `l1' */ + else { + const char *init; /* to search for a `*s2' inside `s1' */ + l2--; /* 1st char will be checked by `memchr' */ + l1 = l1-l2; /* `s2' cannot be found after that */ + while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { + init++; /* 1st char is already checked */ + if (memcmp(init, s2+1, l2) == 0) + return init-1; + else { /* correct `l1' and `s1' to try again */ + l1 -= init-s1; + s1 = init; + } + } + return NULL; /* not found */ + } +} + + +static void push_onecapture (MatchState *ms, int i) { + int l = ms->capture[i].len; + if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); + if (l == CAP_POSITION) + lua_pushnumber(ms->L, (lua_Number)(ms->capture[i].init - ms->src_init + 1)); + else + lua_pushlstring(ms->L, ms->capture[i].init, l); +} + + +static int push_captures (MatchState *ms, const char *s, const char *e) { + int i; + luaL_checkstack(ms->L, ms->level, "too many captures"); + if (ms->level == 0 && s) { /* no explicit captures? */ + lua_pushlstring(ms->L, s, e-s); /* return whole match */ + return 1; + } + else { /* return all captures */ + for (i=0; ilevel; i++) + push_onecapture(ms, i); + return ms->level; /* number of strings pushed */ + } +} + + +static int str_find (lua_State *L) { + size_t l1, l2; + const char *s = luaL_checklstring(L, 1, &l1); + const char *p = luaL_checklstring(L, 2, &l2); + sint32 init = posrelat(luaL_optlong(L, 3, 1), l1) - 1; + if (init < 0) init = 0; + else if ((size_t)(init) > l1) init = (sint32)l1; + if (lua_toboolean(L, 4) || /* explicit request? */ + strpbrk(p, SPECIALS) == NULL) { /* or no special characters? */ + /* do a plain search */ + const char *s2 = lmemfind(s+init, l1-init, p, l2); + if (s2) { + lua_pushnumber(L, (lua_Number)(s2-s+1)); + lua_pushnumber(L, (lua_Number)(s2-s+l2)); + return 2; + } + } + else { + MatchState ms; + int anchor = (*p == '^') ? (p++, 1) : 0; + const char *s1=s+init; + ms.L = L; + ms.src_init = s; + ms.src_end = s+l1; + do { + const char *res; + ms.level = 0; + if ((res=match(&ms, s1, p)) != NULL) { + lua_pushnumber(L, (lua_Number)(s1-s+1)); /* start */ + lua_pushnumber(L, (lua_Number)(res-s)); /* end */ + return push_captures(&ms, NULL, 0) + 2; + } + } while (s1++L; + if (lua_isstring(L, 3)) { + const char *news = lua_tostring(L, 3); + size_t l = lua_strlen(L, 3); + size_t i; + for (i=0; i= 3 && (lua_isstring(L, 3) || lua_isfunction(L, 3)), + 3, "string or function expected"); + luaL_buffinit(L, &b); + ms.L = L; + ms.src_init = src; + ms.src_end = src+srcl; + while (n < max_s) { + const char *e; + ms.level = 0; + e = match(&ms, src, p); + if (e) { + n++; + add_s(&ms, &b, src, e); + } + if (e && e>src) /* non empty match? */ + src = e; /* skip it */ + else if (src < ms.src_end) + luaL_putchar(&b, *src++); + else break; + if (anchor) break; + } + luaL_addlstring(&b, src, ms.src_end-src); + luaL_pushresult(&b); + lua_pushnumber(L, (lua_Number)n); /* number of substitutions */ + return 2; +} + +/* }====================================================== */ + + +/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ +#define MAX_ITEM 512 +/* maximum size of each format specification (such as '%-099.99d') */ +#define MAX_FORMAT 20 + + +static void luaI_addquoted (lua_State *L, luaL_Buffer *b, int arg) { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + luaL_putchar(b, '"'); + while (l--) { + switch (*s) { + case '"': case '\\': case '\n': { + luaL_putchar(b, '\\'); + luaL_putchar(b, *s); + break; + } + case '\0': { + luaL_addlstring(b, "\\000", 4); + break; + } + default: { + luaL_putchar(b, *s); + break; + } + } + s++; + } + luaL_putchar(b, '"'); +} + + +static const char *scanformat (lua_State *L, const char *strfrmt, + char *form, int *hasprecision) { + const char *p = strfrmt; + while (strchr("-+ #0", *p)) p++; /* skip flags */ + if (isdigit(uchar(*p))) p++; /* skip width */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + if (*p == '.') { + p++; + *hasprecision = 1; + if (isdigit(uchar(*p))) p++; /* skip precision */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + } + if (isdigit(uchar(*p))) + luaL_error(L, "invalid format (width or precision too long)"); + if (p-strfrmt+2 > MAX_FORMAT) /* +2 to include `%' and the specifier */ + luaL_error(L, "invalid format (too long)"); + form[0] = '%'; + strncpy(form+1, strfrmt, p-strfrmt+1); + form[p-strfrmt+2] = 0; + return p; +} + + +static int str_format (lua_State *L) { + int arg = 1; + size_t sfl; + const char *strfrmt = luaL_checklstring(L, arg, &sfl); + const char *strfrmt_end = strfrmt+sfl; + luaL_Buffer b; + luaL_buffinit(L, &b); + while (strfrmt < strfrmt_end) { + if (*strfrmt != '%') + luaL_putchar(&b, *strfrmt++); + else if (*++strfrmt == '%') + luaL_putchar(&b, *strfrmt++); /* %% */ + else { /* format item */ + char form[MAX_FORMAT]; /* to store the format (`%...') */ + char buff[MAX_ITEM]; /* to store the formatted item */ + int hasprecision = 0; + if (isdigit(uchar(*strfrmt)) && *(strfrmt+1) == '$') + return luaL_error(L, "obsolete option (d$) to `format'"); + arg++; + strfrmt = scanformat(L, strfrmt, form, &hasprecision); + switch (*strfrmt++) { + case 'c': case 'd': case 'i': { + sprintf(buff, form, luaL_checkint(L, arg)); + break; + } + case 'o': case 'u': case 'x': case 'X': { + sprintf(buff, form, (unsigned int)(luaL_checknumber(L, arg))); + break; + } + case 'e': case 'E': case 'f': + case 'g': case 'G': { + sprintf(buff, form, luaL_checknumber(L, arg)); + break; + } + case 'q': { + luaI_addquoted(L, &b, arg); + continue; /* skip the `addsize' at the end */ + } + case 's': { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + if (!hasprecision && l >= 100) { + /* no precision and string is too long to be formatted; + keep original string */ + lua_pushvalue(L, arg); + luaL_addvalue(&b); + continue; /* skip the `addsize' at the end */ + } + else { + sprintf(buff, form, s); + break; + } + } + default: { /* also treat cases `pnLlh' */ + return luaL_error(L, "invalid option to `format'"); + } + } + luaL_addlstring(&b, buff, strlen(buff)); + } + } + luaL_pushresult(&b); + return 1; +} + + +static const luaL_reg strlib[] = { + {"len", str_len}, + {"sub", str_sub}, + {"lower", str_lower}, + {"upper", str_upper}, + {"char", str_char}, + {"rep", str_rep}, + {"byte", str_byte}, + {"format", str_format}, + {"dump", str_dump}, + {"find", str_find}, + {"gfind", gfind}, + {"gsub", str_gsub}, + {NULL, NULL} +}; + + +/* +** Open string library +*/ +LUALIB_API int luaopen_string (lua_State *L) { + luaL_openlib(L, LUA_STRLIBNAME, strlib, 0); + return 1; +} + diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ltablib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ltablib.c index 2c18bc7f7e..50fd282386 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ltablib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ltablib.c @@ -1,260 +1,260 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include - -#define ltablib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - -#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n)) - - -static int luaB_foreachi (lua_State *L) { - int i; - int n = aux_getn(L, 1); - luaL_checktype(L, 2, LUA_TFUNCTION); - for (i=1; i<=n; i++) { - lua_pushvalue(L, 2); /* function */ - lua_pushnumber(L, (lua_Number)i); /* 1st argument */ - lua_rawgeti(L, 1, i); /* 2nd argument */ - lua_call(L, 2, 1); - if (!lua_isnil(L, -1)) - return 1; - lua_pop(L, 1); /* remove nil result */ - } - return 0; -} - - -static int luaB_foreach (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - luaL_checktype(L, 2, LUA_TFUNCTION); - lua_pushnil(L); /* first key */ - for (;;) { - if (lua_next(L, 1) == 0) - return 0; - lua_pushvalue(L, 2); /* function */ - lua_pushvalue(L, -3); /* key */ - lua_pushvalue(L, -3); /* value */ - lua_call(L, 2, 1); - if (!lua_isnil(L, -1)) - return 1; - lua_pop(L, 2); /* remove value and result */ - } -} - - -static int luaB_getn (lua_State *L) { - lua_pushnumber(L, (lua_Number)aux_getn(L, 1)); - return 1; -} - - -static int luaB_setn (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - luaL_setn(L, 1, luaL_checkint(L, 2)); - return 0; -} - - -static int luaB_tinsert (lua_State *L) { - int v = lua_gettop(L); /* number of arguments */ - int n = aux_getn(L, 1) + 1; - int pos; /* where to insert new element */ - if (v == 2) /* called with only 2 arguments */ - pos = n; /* insert new element at the end */ - else { - pos = luaL_checkint(L, 2); /* 2nd argument is the position */ - if (pos > n) n = pos; /* `grow' array if necessary */ - v = 3; /* function may be called with more than 3 args */ - } - luaL_setn(L, 1, n); /* new size */ - while (--n >= pos) { /* move up elements */ - lua_rawgeti(L, 1, n); - lua_rawseti(L, 1, n+1); /* t[n+1] = t[n] */ - } - lua_pushvalue(L, v); - lua_rawseti(L, 1, pos); /* t[pos] = v */ - return 0; -} - - -static int luaB_tremove (lua_State *L) { - int n = aux_getn(L, 1); - int pos = luaL_optint(L, 2, n); - if (n <= 0) return 0; /* table is `empty' */ - luaL_setn(L, 1, n-1); /* t.n = n-1 */ - lua_rawgeti(L, 1, pos); /* result = t[pos] */ - for ( ;pos= P */ - while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { - if (i>u) luaL_error(L, "invalid order function for sorting"); - lua_pop(L, 1); /* remove a[i] */ - } - /* repeat --j until a[j] <= P */ - while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { - if (j +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include + +#define ltablib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n)) + + +static int luaB_foreachi (lua_State *L) { + int i; + int n = aux_getn(L, 1); + luaL_checktype(L, 2, LUA_TFUNCTION); + for (i=1; i<=n; i++) { + lua_pushvalue(L, 2); /* function */ + lua_pushnumber(L, (lua_Number)i); /* 1st argument */ + lua_rawgeti(L, 1, i); /* 2nd argument */ + lua_call(L, 2, 1); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 1); /* remove nil result */ + } + return 0; +} + + +static int luaB_foreach (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checktype(L, 2, LUA_TFUNCTION); + lua_pushnil(L); /* first key */ + for (;;) { + if (lua_next(L, 1) == 0) + return 0; + lua_pushvalue(L, 2); /* function */ + lua_pushvalue(L, -3); /* key */ + lua_pushvalue(L, -3); /* value */ + lua_call(L, 2, 1); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 2); /* remove value and result */ + } +} + + +static int luaB_getn (lua_State *L) { + lua_pushnumber(L, (lua_Number)aux_getn(L, 1)); + return 1; +} + + +static int luaB_setn (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_setn(L, 1, luaL_checkint(L, 2)); + return 0; +} + + +static int luaB_tinsert (lua_State *L) { + int v = lua_gettop(L); /* number of arguments */ + int n = aux_getn(L, 1) + 1; + int pos; /* where to insert new element */ + if (v == 2) /* called with only 2 arguments */ + pos = n; /* insert new element at the end */ + else { + pos = luaL_checkint(L, 2); /* 2nd argument is the position */ + if (pos > n) n = pos; /* `grow' array if necessary */ + v = 3; /* function may be called with more than 3 args */ + } + luaL_setn(L, 1, n); /* new size */ + while (--n >= pos) { /* move up elements */ + lua_rawgeti(L, 1, n); + lua_rawseti(L, 1, n+1); /* t[n+1] = t[n] */ + } + lua_pushvalue(L, v); + lua_rawseti(L, 1, pos); /* t[pos] = v */ + return 0; +} + + +static int luaB_tremove (lua_State *L) { + int n = aux_getn(L, 1); + int pos = luaL_optint(L, 2, n); + if (n <= 0) return 0; /* table is `empty' */ + luaL_setn(L, 1, n-1); /* t.n = n-1 */ + lua_rawgeti(L, 1, pos); /* result = t[pos] */ + for ( ;pos= P */ + while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (i>u) luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[i] */ + } + /* repeat --j until a[j] <= P */ + while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { + if (j */ -body { background: #FFFFFF; color: #000000; margin-left: 20px; margin-right: 20px; } -body { background: #FFFFFF; color: #000000; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 70%; margin: 0; } -caption, a.elref, a.elref, caption { font-weight: bold; } -div.ah { background-color: #000000; color: #FFFFFF; font-weight: bold; margin-bottom: 3px; margin-top: 3px; } -div.ah{ background: #000000; color: #FFFFFF; font-weight: bold; margin-bottom: 3px; margin-top: 3px; } -div.footer{ background: #B0C2D7; border: 1px solid #436976; color: #000000; font-style: normal; margin-top: 5em; padding-bottom: 3px; text-align: center; } -div.fragment { background-color: #F5F5F5; border: 1px solid #CCCCCC; margin: 4px; padding-left: 4px; width: 98%; } -div.fragment{ background: #FFFFCC; border: 1px solid #CCCCCC; margin-left: 2em; margin-right: 2em; } -pre { font-size: 120%; margin:2px 1px 3px 1px; } -div.groupheader { font-weight: bold; margin-bottom: 6px; margin-left: 16px; margin-top: 12px; } -div.groupheader{ border-bottom: 1px solid #557E8C; font-weight: bold; margin-bottom: 6px; margin-left: 16px; margin-top: 12px; } -div.grouptext{ font-style: italic; margin-left: 16px; } -div.header{ background: #B0C2D7; border: 1px solid #436976; color: #000000; font-style: normal; margin-bottom: 1em; padding-bottom: 3px; padding-left: 10px; text-align: left; text-transform: lowercase; } -div.header a, div.footer a{ color: #436976; font-weight: bold; white-space: nowrap; } -div.header a {margin-right: 1em; } -div.header a:visited, div.footer a:visited { color: #436976; font-weight: bold; } -div.header a:hover, div.footer a:hover { color: #B52010; font-weight: bold; text-decoration: none; } -div.index { padding-right: 10px; text-align: right; } -div.maindox { margin: 2%; width: 96%; } -div.nav { float: left; } -div.qindex { background-color: #EEEEFF; border: 4px solid #EEEEFF; margin-bottom: 2px; text-align: center; width: 100%; } -dl.el { margin-left: -1cm; } -font.charliteral, span.charliteral { color: #008080; } -font.comment, span.comment { color: #800000; } -font.keyword, span.keyword { color: #008000; } -font.keywordflow, span.keywordflow { color: #E08000; } -font.keywordtype, span.keywordtype { color: #604020; } -font.preprocessor, span.preprocessor { color: #806020; } -font.stringliteral, span.stringliteral { color: #002080; } -h1{ font-size: 220%; text-align: center; } -h2{ border-bottom: 2px solid #557E8C; font-size: 170%; } -hr { border: 1px solid #000000; } -img.formuladsp { } -img.formulainl { vertical-align: middle; } -p.formuladsp { text-align: center; } -td { font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 70%; } -td.indexkey{ background: #EEEEFF; font-weight: bold; margin: 2px 0px; padding: 2px 10px; } -td.indexvalue{ background: #EEEEFF; font-style: italic; margin: 2px 0px; padding: 2px 10px; } -td.md{ font-weight: bold; } -td.mdname, td.mdname1 { color: #FF9900; font-weight: bold; } -tr.memlist { background-color: #F0F0F0; } -ul { list-style-type: square; } -.mdescleft { background-color: #FAFAFA; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-top: 1px none #E0E0E0; margin: 0px; padding-left: 8px; } -.mdescright { background-color: #FAFAFA; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-top: 1px none #E0E0E0; font-style: italic; margin: 0px; padding-bottom: 0px; padding-left: 4px; padding-right: 8px; } -.mdrow { padding: 8px 20px; } -.mdtable { background: #EEEEEE; border: 1px solid #CCCCCC; } -.memitemleft { background-color: #FAFAFA; border-top-color: #FFFFFF; border-top-style: solid; border-top-width: 2px; font-family: Geneva, Arial, Helvetica, sans-serif; font-size: 12px; margin: 4px; padding: 1px 0px 0px 8px; } -.memitemright { background-color: #FAFAFA; border-top-color: #FFFFFF; border-top-style: solid; border-top-width: 2px; font-family: Geneva, Arial, Helvetica, sans-serif; font-size: 13px; margin: 4px; padding: 1px 0px 0px 8px; } +a.code { color: #4444EE; font-weight: normal; text-decoration: none; } +a.coderef { color: #4444EE; font-weight: normal; } +a:link { color: #0066CC; text-decoration: none; } +a:visited { color: #436976; text-decoration: none; } +a.code{ color: #436976; font-weight: normal; text-decoration: none; } +a.coderef{ color: #436976; font-weight: normal; } +a.qindex{ font-weight: normal; white-space: nowrap; } +a.qindex:hover, a.qindexref:hover { background-color: #DDDDFF; text-decoration: none; } +a.qindexhl, a.qindexrefhl { background-color: #6666CC; color: #FFFFFF; font-weight: bold; text-decoration: none; } +a.qindexhl:hover, a.qindexrefhl:hover { background-color: #6666CC; text-decoration: none; } +a.qindexref{ } +a:hover { color: #B52010; color: #B52010; text-decoration: underline; text-decoration: underline; } +a.el, a.qindex, a.qindexref, a.el { font-weight: bold; text-decoration: none; } +/* this is repeated to fix a difference in browser rendering, perhaps should be set for each class of */ +body { background: #FFFFFF; color: #000000; margin-left: 20px; margin-right: 20px; } +body { background: #FFFFFF; color: #000000; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 70%; margin: 0; } +caption, a.elref, a.elref, caption { font-weight: bold; } +div.ah { background-color: #000000; color: #FFFFFF; font-weight: bold; margin-bottom: 3px; margin-top: 3px; } +div.ah{ background: #000000; color: #FFFFFF; font-weight: bold; margin-bottom: 3px; margin-top: 3px; } +div.footer{ background: #B0C2D7; border: 1px solid #436976; color: #000000; font-style: normal; margin-top: 5em; padding-bottom: 3px; text-align: center; } +div.fragment { background-color: #F5F5F5; border: 1px solid #CCCCCC; margin: 4px; padding-left: 4px; width: 98%; } +div.fragment{ background: #FFFFCC; border: 1px solid #CCCCCC; margin-left: 2em; margin-right: 2em; } +pre { font-size: 120%; margin:2px 1px 3px 1px; } +div.groupheader { font-weight: bold; margin-bottom: 6px; margin-left: 16px; margin-top: 12px; } +div.groupheader{ border-bottom: 1px solid #557E8C; font-weight: bold; margin-bottom: 6px; margin-left: 16px; margin-top: 12px; } +div.grouptext{ font-style: italic; margin-left: 16px; } +div.header{ background: #B0C2D7; border: 1px solid #436976; color: #000000; font-style: normal; margin-bottom: 1em; padding-bottom: 3px; padding-left: 10px; text-align: left; text-transform: lowercase; } +div.header a, div.footer a{ color: #436976; font-weight: bold; white-space: nowrap; } +div.header a {margin-right: 1em; } +div.header a:visited, div.footer a:visited { color: #436976; font-weight: bold; } +div.header a:hover, div.footer a:hover { color: #B52010; font-weight: bold; text-decoration: none; } +div.index { padding-right: 10px; text-align: right; } +div.maindox { margin: 2%; width: 96%; } +div.nav { float: left; } +div.qindex { background-color: #EEEEFF; border: 4px solid #EEEEFF; margin-bottom: 2px; text-align: center; width: 100%; } +dl.el { margin-left: -1cm; } +font.charliteral, span.charliteral { color: #008080; } +font.comment, span.comment { color: #800000; } +font.keyword, span.keyword { color: #008000; } +font.keywordflow, span.keywordflow { color: #E08000; } +font.keywordtype, span.keywordtype { color: #604020; } +font.preprocessor, span.preprocessor { color: #806020; } +font.stringliteral, span.stringliteral { color: #002080; } +h1{ font-size: 220%; text-align: center; } +h2{ border-bottom: 2px solid #557E8C; font-size: 170%; } +hr { border: 1px solid #000000; } +img.formuladsp { } +img.formulainl { vertical-align: middle; } +p.formuladsp { text-align: center; } +td { font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 70%; } +td.indexkey{ background: #EEEEFF; font-weight: bold; margin: 2px 0px; padding: 2px 10px; } +td.indexvalue{ background: #EEEEFF; font-style: italic; margin: 2px 0px; padding: 2px 10px; } +td.md{ font-weight: bold; } +td.mdname, td.mdname1 { color: #FF9900; font-weight: bold; } +tr.memlist { background-color: #F0F0F0; } +ul { list-style-type: square; } +.mdescleft { background-color: #FAFAFA; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-top: 1px none #E0E0E0; margin: 0px; padding-left: 8px; } +.mdescright { background-color: #FAFAFA; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-top: 1px none #E0E0E0; font-style: italic; margin: 0px; padding-bottom: 0px; padding-left: 4px; padding-right: 8px; } +.mdrow { padding: 8px 20px; } +.mdtable { background: #EEEEEE; border: 1px solid #CCCCCC; } +.memitemleft { background-color: #FAFAFA; border-top-color: #FFFFFF; border-top-style: solid; border-top-width: 2px; font-family: Geneva, Arial, Helvetica, sans-serif; font-size: 12px; margin: 4px; padding: 1px 0px 0px 8px; } +.memitemright { background-color: #FAFAFA; border-top-color: #FFFFFF; border-top-style: solid; border-top-width: 2px; font-family: Geneva, Arial, Helvetica, sans-serif; font-size: 13px; margin: 4px; padding: 1px 0px 0px 8px; } diff --git a/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_input.h b/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_input.h index c2c32b8f27..8a5283ed78 100644 --- a/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_input.h +++ b/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_input.h @@ -17,7 +17,7 @@ //------------------------------------------------------------------------------------------------- // project: sadge lib // file: text_input.h -// version: $Id$ +// version: $Id: text_input.h,v 1.2 2004/06/15 17:33:37 boucher Exp $ // //------------------------------------------------------------------------------------------------- diff --git a/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_output.h b/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_output.h index 5d236e61e5..ba77ab9e24 100644 --- a/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_output.h +++ b/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_output.h @@ -20,7 +20,7 @@ //------------------------------------------------------------------------------------------------- // project: sadge lib // file: text_output.h -// version: $Id$ +// version: $Id: text_output.h,v 1.1 2004/02/10 17:54:46 boucher Exp $ // // This file contains a string class derived from the STL string // The string compare functions od the class are case insensitive From d5d501c86bf71c4857dc1bab5e12bc3fd5624e19 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 20:59:06 +0000 Subject: [PATCH 09/24] Phase 1: Add landscape support to decal system via CShadowPolyReceiver::receiveDecal() Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/include/nel/3d/render_trav.h | 3 + nel/include/nel/3d/shadow_poly_receiver.h | 9 ++ nel/src/3d/decal.cpp | 15 +++- nel/src/3d/shadow_poly_receiver.cpp | 100 ++++++++++++++++++++++ 4 files changed, 126 insertions(+), 1 deletion(-) diff --git a/nel/include/nel/3d/render_trav.h b/nel/include/nel/3d/render_trav.h index 9c6403b469..66961c5ce9 100644 --- a/nel/include/nel/3d/render_trav.h +++ b/nel/include/nel/3d/render_trav.h @@ -257,6 +257,9 @@ class CRenderTrav : public CTravCameraScene // add a landscape. Special for CLandscapeModel::traverseRender(); void addRenderLandscape(CLandscapeModel *model); + /// Get list of landscape models registered this frame (for decal face collection). + const std::vector &getLandscapeRenderList() const { return _LandscapeRenderList; } + /// \name Temp Debug //@{ diff --git a/nel/include/nel/3d/shadow_poly_receiver.h b/nel/include/nel/3d/shadow_poly_receiver.h index 5c94768492..e6aac8c9f1 100644 --- a/nel/include/nel/3d/shadow_poly_receiver.h +++ b/nel/include/nel/3d/shadow_poly_receiver.h @@ -30,6 +30,7 @@ namespace NL3D { class IDriver; class CMaterial; class CShadowMap; +class CDecalContext; // *************************************************************************** @@ -99,6 +100,14 @@ class CShadowPolyReceiver std::vector &destTris, bool colorUpfacingVertices); + /** Collect triangles for a projected decal. + * Selects triangles from the quad grid, clips per the decal context's planes, + * and outputs raw CVector triangles to CDecalContext::DestTris. + * \param cdc Decal context with clip planes, bounding box, and destination output. + * \param vertDelta Offset to add to vertices (for landscape tile positioning). + */ + void receiveDecal(CDecalContext &cdc, const CVector &vertDelta); + /** Use the triangles added for camera 3rd person collision * return a [0,1] value. 0 => collision at start. 1 => no collision. * \param testType is the type of intersection: simple ray, cylinder or cone diff --git a/nel/src/3d/decal.cpp b/nel/src/3d/decal.cpp index add338fffa..6d7b8e2bb1 100644 --- a/nel/src/3d/decal.cpp +++ b/nel/src/3d/decal.cpp @@ -31,6 +31,8 @@ #include "nel/3d/driver.h" #include "nel/3d/clip_trav.h" #include "nel/3d/visual_collision_manager.h" +#include "nel/3d/render_trav.h" +#include "nel/3d/landscape_model.h" using namespace std; using namespace NL3D; @@ -233,11 +235,22 @@ context.WorldClipPlanes[i].invert(); context.WorldMatrix = getWorldMatrix(); -// Clear and collect triangles via visual collision +// Clear and collect triangles via visual collision (objects/meshes) _Vertices.clear(); context.DestTris = &_Vertices; vcm->receiveDecal(context); +// Also collect triangles from landscape's shadow poly receiver (terrain) +CRenderTrav &renderTrav = sc->getRenderTrav(); +const std::vector &landscapes = renderTrav.getLandscapeRenderList(); +for (uint i = 0; i < landscapes.size(); ++i) +{ +CLandscapeModel *lm = landscapes[i]; +if (!lm) continue; +CVector vertDelta = -lm->Landscape.getPZBModelPosition(); +lm->Landscape.getShadowPolyReceiver().receiveDecal(context, vertDelta); +} + // Generate UV coordinates from collected vertices generateUVs(); } diff --git a/nel/src/3d/shadow_poly_receiver.cpp b/nel/src/3d/shadow_poly_receiver.cpp index 7091310f70..8d370c3c22 100644 --- a/nel/src/3d/shadow_poly_receiver.cpp +++ b/nel/src/3d/shadow_poly_receiver.cpp @@ -20,6 +20,7 @@ #include "nel/misc/polygon.h" #include "nel/3d/shadow_poly_receiver.h" #include "nel/3d/shadow_map.h" +#include "nel/3d/decal.h" #include "nel/3d/driver.h" #include "nel/3d/camera_col.h" @@ -542,6 +543,105 @@ void CShadowPolyReceiver::renderWithPolyClip(IDriver *drv, CMaterial &shadowMa renderSelection(drv, shadowMat, shadowMap, casterPos, vertDelta); } +// *************************************************************************** +void CShadowPolyReceiver::receiveDecal(CDecalContext &cdc, const CVector &vertDelta) +{ + // Select triangles from the quad grid using the decal's bounding box + _TriangleGrid.select(cdc.WorldBBox.getMin(), cdc.WorldBBox.getMax()); + if (_TriangleGrid.begin() == _TriangleGrid.end()) return; + + nlassert(cdc.DestTris != NULL); + + uint i, j; + + // Reset vertex flags for selected triangles + TTriangleGrid::CIterator it; + for (it = _TriangleGrid.begin(); it != _TriangleGrid.end(); it++) + { + CTriangleId &triId = *it; + for (i = 0; i < 3; i++) + { + _Vertices[triId.Vertex[i]].Flags = 0; + } + } + + // Clip planes from decal context (max NL3D_SPR_NUM_CLIP_PLANE) + uint numClipPlanes = std::min((uint)cdc.WorldClipPlanes.size(), (uint)NL3D_SPR_NUM_CLIP_PLANE); + + // Collect visible triangles (those not fully clipped by all planes) + static std::vector visibleTris; + visibleTris.clear(); + + for (it = _TriangleGrid.begin(); it != _TriangleGrid.end(); it++) + { + CTriangleId &triId = *it; + uint triFlag = NL3D_SPR_NUM_CLIP_PLANE_MASK; + + CVectorId *vid[3] = { + &_Vertices[triId.Vertex[0]], + &_Vertices[triId.Vertex[1]], + &_Vertices[triId.Vertex[2]] + }; + + for (i = 0; i < 3; i++) + { + if (!vid[i]->Flags) + { + for (j = 0; j < numClipPlanes; j++) + { + bool out = cdc.WorldClipPlanes[j] * *vid[i] > 0; + vid[i]->Flags |= ((uint)out) << j; + } + vid[i]->Flags |= NL3D_SPR_NUM_CLIP_PLANE_SHIFT; + } + triFlag &= vid[i]->Flags; + } + + // If triangle not fully clipped (at least one vertex inside all planes) + if ((triFlag & NL3D_SPR_NUM_CLIP_PLANE_MASK) == 0) + { + visibleTris.push_back(&triId); + } + } + + // Clip and output triangles based on decal clipping mode + if (cdc.ClipMode == DecalClipGeometry) + { + // Geometry clipping: clip each triangle's polygon against the clip planes + static NLMISC::CPolygon clippedTri; + + for (uint triIndex = 0; triIndex < visibleTris.size(); ++triIndex) + { + CTriangleId &triId = *visibleTris[triIndex]; + clippedTri.Vertices.resize(3); + clippedTri.Vertices[0] = _Vertices[triId.Vertex[0]]; + clippedTri.Vertices[1] = _Vertices[triId.Vertex[1]]; + clippedTri.Vertices[2] = _Vertices[triId.Vertex[2]]; + clippedTri.clip(cdc.WorldClipPlanes); + if (clippedTri.Vertices.size() >= 3) + { + for (uint k = 0; k < clippedTri.Vertices.size() - 2; ++k) + { + cdc.DestTris->push_back(clippedTri.Vertices[0] + vertDelta); + cdc.DestTris->push_back(clippedTri.Vertices[k + 1] + vertDelta); + cdc.DestTris->push_back(clippedTri.Vertices[k + 2] + vertDelta); + } + } + } + } + else + { + // No clipping or mask clipping: output full triangles + for (uint triIndex = 0; triIndex < visibleTris.size(); ++triIndex) + { + CTriangleId &triId = *visibleTris[triIndex]; + cdc.DestTris->push_back(_Vertices[triId.Vertex[0]] + vertDelta); + cdc.DestTris->push_back(_Vertices[triId.Vertex[1]] + vertDelta); + cdc.DestTris->push_back(_Vertices[triId.Vertex[2]] + vertDelta); + } + } +} + // *************************************************************************** float CShadowPolyReceiver::getCameraCollision(const CVector &start, const CVector &end, TCameraColTest testType, float radius) { From b97ab8bd77faaf315c3b0cd93c34e18705122ea7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 21:21:03 +0000 Subject: [PATCH 10/24] Phase 2: Port vertex program from legacy decal (distance fade, Z blend, per-vertex color) Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/include/nel/3d/decal.h | 75 +++++++++++ nel/include/nel/3d/decal_manager.h | 60 ++++++++- nel/include/nel/3d/u_decal.h | 13 ++ nel/src/3d/decal.cpp | 114 +++++++++++++++- nel/src/3d/decal_manager.cpp | 210 ++++++++++++++++++++++++++--- nel/src/3d/u_decal.cpp | 28 ++++ 6 files changed, 474 insertions(+), 26 deletions(-) diff --git a/nel/include/nel/3d/decal.h b/nel/include/nel/3d/decal.h index 90f4bb9d0b..79789766d8 100644 --- a/nel/include/nel/3d/decal.h +++ b/nel/include/nel/3d/decal.h @@ -108,6 +108,9 @@ class CDecalContext * Face selection uses quad-grid + clip-plane refinement via CVisualCollisionMesh. * UV coordinates are generated from the inverse world matrix of the unit cube (§4.5.3). * + * Supports a vertex program for distance-based attenuation, bottom/top Z blending, + * and per-vertex diffuse color (ported from the legacy CLegacyDecal system). + * * \author Christopher Tarento * \author Nevrax France * \date 2007 @@ -168,6 +171,14 @@ class CDecal : public CTransform */ const std::vector &getUVs() const { return _UVs; } + /** Get per-vertex RGBA colors corresponding to the vertices. + * Valid after calling getVertices(). Contains diffuse color + computed alpha + * (distance attenuation × bottom blend × top blend). + * Used by the CPU fallback path when vertex programs are not available. + * \return vector of RGBA colors (one per vertex) + */ + const std::vector &getColors() const { return _Colors; } + /** Set UV sub-region within a texture atlas. * \param uv1 Top-left UV coordinate * \param uv2 Bottom-right UV coordinate @@ -192,6 +203,53 @@ class CDecal : public CTransform /// Return whether this decal is marked static. bool isStatic() const { return _IsStatic; } + /** Set the diffuse color applied to the decal. + * The RGB components tint the texture, alpha is a base opacity. + * \param diffuse RGBA diffuse color + */ + void setDiffuse(NLMISC::CRGBA diffuse) { _Diffuse = diffuse; } + + /// Get the current diffuse color. + NLMISC::CRGBA getDiffuse() const { return _Diffuse; } + + /** Set the emissive color added to the decal. + * Added on top of the texture × diffuse result. + * \param emissive RGBA emissive color + */ + void setEmissive(NLMISC::CRGBA emissive); + + /// Get the current emissive color. + NLMISC::CRGBA getEmissive() const { return _Emissive; } + + /** Set the bottom Z-blend region. + * Decal alpha fades from 0 at zMin to 1 at zMax (bottom edge). + * \param zMin Altitude below which the decal is fully transparent + * \param zMax Altitude above which bottom blend is fully opaque + */ + void setBottomBlend(float zMin, float zMax); + + /** Set the top Z-blend region. + * Decal alpha fades from 1 at zMin to 0 at zMax (top edge). + * \param zMin Altitude below which top blend is fully opaque + * \param zMax Altitude above which the decal is fully transparent + */ + void setTopBlend(float zMin, float zMax); + + /// Get bottom blend zMin. + float getBottomBlendZMin() const { return _BottomBlendZMin; } + /// Get bottom blend zMax. + float getBottomBlendZMax() const { return _BottomBlendZMax; } + /// Get top blend zMin. + float getTopBlendZMin() const { return _TopBlendZMin; } + /// Get top blend zMax. + float getTopBlendZMax() const { return _TopBlendZMax; } + + /** Get the world-to-UV matrix rows for the vertex program. + * Row 0 maps world X to U, Row 1 maps world Y to V. + * Set up during generateUVs() for the VP path. + */ + const CMatrix &getWorldToUVMatrix() const { return _WorldToUVMatrix; } + /** Get the Matrix that transforms local coordinates to UV coordinates. * Maps (x,y)=(0,0) to (u,v)=(0,1) and (x,y)=(0,1) to (u,v)=(0,0) * in local decal space (unit cube). See PDF §4.5.3. @@ -220,6 +278,13 @@ class CDecal : public CTransform */ void generateUVs(); + /** Compute per-vertex colors for the CPU fallback path. + * Applies diffuse color, distance attenuation, and bottom/top Z blending. + * \param distScale Linear distance attenuation scale factor + * \param distBias Linear distance attenuation bias + */ + void computeColors(float distScale, float distBias); + private: CMaterial _Mat; uint32 _MaterialId; @@ -232,11 +297,21 @@ class CDecal : public CTransform std::vector _Vertices; std::vector _UVs; + std::vector _Colors; bool _IsStatic; CUV _UV1; CUV _UV2; CDecalContext _DecalContext; + + NLMISC::CRGBA _Diffuse; + NLMISC::CRGBA _Emissive; + float _BottomBlendZMin; + float _BottomBlendZMax; + float _TopBlendZMin; + float _TopBlendZMax; + + CMatrix _WorldToUVMatrix; }; }//NL3D diff --git a/nel/include/nel/3d/decal_manager.h b/nel/include/nel/3d/decal_manager.h index 03976e59c0..863540dff1 100644 --- a/nel/include/nel/3d/decal_manager.h +++ b/nel/include/nel/3d/decal_manager.h @@ -32,6 +32,7 @@ #include "nel/3d/decal.h" #include "nel/3d/material.h" #include "nel/3d/vertex_buffer.h" +#include "nel/3d/vertex_program.h" #include "nel/3d/driver.h" @@ -46,15 +47,57 @@ class CScene; const uint32 NL3D_DECAL_VB_MAX_VERTICES = 4096 * 3; +// *************************************************************************** +/** + * Vertex program for decal distance attenuation, bottom/top Z blending, + * and diffuse color modulation. + * + * Ported from the legacy CLegacyDecal system's DecalAttenuationVertexProgram. + * + * Constants: + * c[0-3]: ModelViewProjection matrix + * c[4]: WorldToUV row 0 (world X,Y,Z,W → U) + * c[5]: WorldToUV row 1 (world X,Y,Z,W → V) + * c[6]: Camera position (world space, relative to model origin) + * c[7]: DistScaleBias (x=scale, y=bias, z=0.0, w=1.0) + * c[8]: Diffuse color (RGB normalized to [0,1]) + * c[11]: BlendScale (x=bottomScale, y=bottomBias, z=topScale, w=topBias) + */ +class CVertexProgramDecalAttenuation : public CVertexProgram +{ +public: + struct CIdx + { + uint WorldToUV0; + uint WorldToUV1; + uint RefCamDist; + uint DistScaleBias; + uint Diffuse; + uint BlendScale; + }; + + CVertexProgramDecalAttenuation(); + ~CVertexProgramDecalAttenuation(); + virtual void buildInfo(); + inline const CIdx &idx() const { return m_Idx; } + +private: + CIdx m_Idx; +}; + + // *************************************************************************** /** * Decal Manager: collects decals per material and renders them in batched draw calls. * * Each frame, CDecal::traverseRender() registers decals here. At flush time, * the manager iterates per-material groups, fills a fixed-size AGP volatile - * vertex buffer (position + UV), and issues renderRawTriangles() calls. + * vertex buffer (position + UV + color), and issues renderRawTriangles() calls. * If the buffer overflows mid-decal, it is flushed and refilled (see PDF §.6.3). * + * Supports a vertex program path (attenuation, Z blending, diffuse) and a CPU + * fallback path using per-vertex colors. + * * \author Christopher Tarento * \author Nevrax France * \date 2007 @@ -92,6 +135,14 @@ class CDecalManager */ void setVertexProgram(const bool b) { _UseVertexProgram = b; } + /** Set distance attenuation parameters (affects all decals). + * At distance d from camera: alpha *= d * scale + bias. + * Typically scale = -factor/maxDist, bias = factor. + * \param scale Distance scale factor + * \param bias Distance bias + */ + void setDistAttenuation(float scale, float bias) { _DistScale = scale; _DistBias = bias; } + private: /// A registered material with its ID struct CRegisteredMaterial @@ -109,9 +160,14 @@ class CDecalManager uint32 _NextMaterialId; bool _UseVertexProgram; + float _DistScale; + float _DistBias; - /// Fixed-size AGP volatile vertex buffer (Position + TexCoord0) + /// Fixed-size AGP volatile vertex buffer (Position + TexCoord0 + PrimaryColor) CVertexBuffer _VB; + + /// The decal attenuation vertex program (shared instance) + NLMISC::CSmartPtr _VertexProgram; }; diff --git a/nel/include/nel/3d/u_decal.h b/nel/include/nel/3d/u_decal.h index 2d09045663..e7d6211813 100644 --- a/nel/include/nel/3d/u_decal.h +++ b/nel/include/nel/3d/u_decal.h @@ -25,6 +25,7 @@ #define NL_U_DECAL_H #include "nel/misc/types_nl.h" +#include "nel/misc/rgba.h" #include "nel/misc/uv.h" #include "nel/3d/u_transform.h" @@ -64,6 +65,18 @@ class UDecal : public UTransform /// Mark this decal as static (only recomputed once) void setStatic(bool isStatic); + + /// Set the diffuse color (RGB tints texture, A is base opacity) + void setDiffuse(NLMISC::CRGBA diffuse); + + /// Set the emissive color (added to texture × diffuse) + void setEmissive(NLMISC::CRGBA emissive); + + /// Set the bottom Z-blend region (fade from 0 at zMin to 1 at zMax) + void setBottomBlend(float zMin, float zMax); + + /// Set the top Z-blend region (fade from 1 at zMin to 0 at zMax) + void setTopBlend(float zMin, float zMax); }; diff --git a/nel/src/3d/decal.cpp b/nel/src/3d/decal.cpp index 6d7b8e2bb1..a7ad992fe2 100644 --- a/nel/src/3d/decal.cpp +++ b/nel/src/3d/decal.cpp @@ -50,7 +50,13 @@ _Touched(true), _StableFrameCount(0), _IsStatic(false), _UV1(CUV(0, 0)), -_UV2(CUV(1, 1)) +_UV2(CUV(1, 1)), +_Diffuse(CRGBA::White), +_Emissive(CRGBA::Black), +_BottomBlendZMin(-1e10f), +_BottomBlendZMax(-1e10f), +_TopBlendZMin(1e10f), +_TopBlendZMax(1e10f) { setOpacity(true); setTransparency(false); @@ -64,13 +70,28 @@ _Mat.setDstBlend(CMaterial::invsrcalpha); _Mat.setZWrite(false); _Mat.setDoubleSided(true); _Mat.setZBias(-0.06f); +_Mat.setAlphaTest(true); +_Mat.setAlphaTestThreshold(1.f / 255.f); -// Texture environment: output = texture color/alpha -_Mat.texConstantColor(0, CRGBA::White); -_Mat.texEnvOpRGB(0, CMaterial::Replace); +// Stage 0: diffuse color applied to texture +// RGB = Texture * Diffuse, Alpha = Diffuse * Texture +_Mat.texEnvOpRGB(0, CMaterial::Modulate); _Mat.texEnvArg0RGB(0, CMaterial::Texture, CMaterial::SrcColor); -_Mat.texEnvOpAlpha(0, CMaterial::Replace); -_Mat.texEnvArg0Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha); +_Mat.texEnvArg1RGB(0, CMaterial::Diffuse, CMaterial::SrcColor); +_Mat.texEnvOpAlpha(0, CMaterial::Modulate); +_Mat.texEnvArg0Alpha(0, CMaterial::Diffuse, CMaterial::SrcAlpha); +_Mat.texEnvArg1Alpha(0, CMaterial::Texture, CMaterial::SrcAlpha); + +// Stage 1: add emissive color +// RGB = Previous + Constant, Alpha = Previous * Constant +_Mat.texEnvOpRGB(1, CMaterial::Add); +_Mat.texEnvArg0RGB(1, CMaterial::Previous, CMaterial::SrcColor); +_Mat.texEnvArg1RGB(1, CMaterial::Constant, CMaterial::SrcColor); +_Mat.texEnvOpAlpha(1, CMaterial::Modulate); +_Mat.texEnvArg0Alpha(1, CMaterial::Previous, CMaterial::SrcAlpha); +_Mat.texEnvArg1Alpha(1, CMaterial::Constant, CMaterial::SrcAlpha); + +setEmissive(CRGBA::Black); // Default clipping mode: geometry clipping (best fillrate, see PDF 4.5.2) _DecalContext.ClipMode = DecalClipGeometry; @@ -108,6 +129,33 @@ _Mat.setTexture(0, tex); } +// *************************************************************************** +void CDecal::setEmissive(NLMISC::CRGBA emissive) +{ +_Emissive = emissive; +// Set the stage 1 constant color to the emissive value +_Mat.texConstantColor(1, CRGBA(emissive.R, emissive.G, emissive.B, 255)); +} + + +// *************************************************************************** +void CDecal::setBottomBlend(float zMin, float zMax) +{ +if (zMin > zMax) std::swap(zMin, zMax); +_BottomBlendZMin = zMin; +_BottomBlendZMax = zMax; +} + + +// *************************************************************************** +void CDecal::setTopBlend(float zMin, float zMax) +{ +if (zMin > zMax) std::swap(zMin, zMax); +_TopBlendZMin = zMin; +_TopBlendZMax = zMax; +} + + // *************************************************************************** // Clip method: bounding sphere vs frustum test (see PDF .6.1) bool CDecal::clip() @@ -253,13 +301,21 @@ lm->Landscape.getShadowPolyReceiver().receiveDecal(context, vertDelta); // Generate UV coordinates from collected vertices generateUVs(); + +// Compute per-vertex colors for CPU fallback path +if (!useVertexProgram) +{ +CDecalManager &mgr = sc->getRenderTrav().getDecalManager(); +(void)mgr; // distScale/distBias set externally +computeColors(0.f, 1.f); +} } // *************************************************************************** // UV coordinate generation (see PDF 4.5.3) // Uses inverse world matrix to project world-space vertices back to unit-cube local space. -// Camera position is subtracted for numerical stability (4.6.1). +// Also builds the worldToUV matrix for the VP path. void CDecal::generateUVs() { _UVs.resize(_Vertices.size()); @@ -269,6 +325,7 @@ return; // Compute worldToUV matrix: maps world position to [0,1] UV in local decal space CMatrix invWorld = getWorldMatrix().inverted(); +_WorldToUVMatrix = invWorld; for (uint i = 0; i < _Vertices.size(); ++i) { @@ -285,6 +342,49 @@ _UVs[i].V = v; } +// *************************************************************************** +// Compute per-vertex colors for CPU fallback path +// Applies diffuse color, distance attenuation, and bottom/top Z blending. +void CDecal::computeColors(float distScale, float distBias) +{ +_Colors.resize(_Vertices.size()); + +if (_Vertices.empty()) +return; + +const CVector camPos = getOwnerScene()->getCam()->getMatrix().getPos(); + +float bottomBlendScale = 1.f / NLMISC::favoid0(_BottomBlendZMax - _BottomBlendZMin); +float topBlendScale = 1.f / NLMISC::favoid0(_TopBlendZMin - _TopBlendZMax); + +for (uint i = 0; i < _Vertices.size(); ++i) +{ +const CVector &v = _Vertices[i]; + +// Distance attenuation +float dist = (camPos - v).norm(); +float intensity = dist * distScale + distBias; +clamp(intensity, 0.f, 1.f); + +// Bottom blend +float bottomBlend = (v.z - _BottomBlendZMin) * bottomBlendScale; +clamp(bottomBlend, 0.f, 1.f); +intensity *= bottomBlend; + +// Top blend +float topBlend = (v.z - _TopBlendZMax) * topBlendScale; +clamp(topBlend, 0.f, 1.f); +intensity *= topBlend; + +// Apply to diffuse color +_Colors[i].R = _Diffuse.R; +_Colors[i].G = _Diffuse.G; +_Colors[i].B = _Diffuse.B; +_Colors[i].A = (uint8)((float)_Diffuse.A * intensity); +} +} + + // *************************************************************************** void CDecal::setUVCoord(const CUV uv1, const CUV uv2) { diff --git a/nel/src/3d/decal_manager.cpp b/nel/src/3d/decal_manager.cpp index 74767236c4..98ec6ebcd5 100644 --- a/nel/src/3d/decal_manager.cpp +++ b/nel/src/3d/decal_manager.cpp @@ -2,6 +2,8 @@ * Batched decal rendering manager for NeL 3D. * * Implements the rendering pseudocode from the intern report (§.6.3). + * Includes the vertex program for distance attenuation, Z blending, + * and diffuse color (ported from the legacy CLegacyDecal system). */ /* Copyright, 2007 Nevrax Ltd. @@ -32,15 +34,107 @@ using namespace NLMISC; using namespace NL3D; +// *************************************************************************** +// Vertex program assembly code for decal attenuation (ported from legacy system) +// Constants: +// c[0-3]: ModelViewProjection matrix +// c[4]: WorldToUV row 0 (→ tex U) +// c[5]: WorldToUV row 1 (→ tex V) +// c[6]: Camera position (relative to model origin) +// c[7]: DistScaleBias (x=scale, y=bias, z=0.0, w=1.0) +// c[8]: Diffuse color (RGB in [0,1]) +// c[11]: BlendScale (x=bottomScale, y=bottomBias, z=topScale, w=topBias) +static const char *DecalAttenuationVertexProgramCode = +"!!VP1.0\n\ + DP4 o[HPOS].x, c[0], v[0]; #transform vertex in view space\n\ + DP4 o[HPOS].y, c[1], v[0];\n\ + DP4 o[HPOS].z, c[2], v[0];\n\ + DP4 o[HPOS].w, c[3], v[0];\n\ + # transform texcoord 0\n\ + DP4 o[TEX0].x, c[4], v[0];\n\ + DP4 o[TEX0].y, c[5], v[0];\n\ + #compute distance from camera\n\ + ADD R0, v[0], -c[6];\n\ + DP3 R0.x, R0, R0;\n\ + RSQ R0.x, R0.x;\n\ + RCP R0.x, R0.x;\n\ + MUL o[COL0].xyz, c[8], v[3];\n\ + #compute attenuation with distance\n\ + MAD R0.w, R0.x, c[7].x, c[7].y;\n\ + # clamp in [0, 1]\n\ + MIN R0.w, R0.w, c[7].w;\n\ + MAX R0.w, R0.w, c[7].z;\n\ + #compute bottom blend\n\ + MAD R1.x, v[0].z, c[11].x, c[11].y;\n\ + MIN R1.x, R1.x, c[7].w;\n\ + MAX R1.x, R1.x, c[7].z;\n\ + MUL R0.w, R1.x, R0.w;\n\ + #compute top blend\n\ + MAD R1.x, v[0].z, c[11].z, c[11].w;\n\ + MIN R1.x, R1.x, c[7].w;\n\ + MAX R1.x, R1.x, c[7].z;\n\ + MUL R0.w, R1.x, R0.w;\n\ + #apply vertex alpha\n\ + MUL o[COL0].w, v[3], R0.w;\n\ + END \n"; + + +// *************************************************************************** +CVertexProgramDecalAttenuation::CVertexProgramDecalAttenuation() +{ + CSource *source = new CSource(); + source->Profile = nelvp; + source->DisplayName = "nelvp/DecalAttenuation"; + source->setSourcePtr(DecalAttenuationVertexProgramCode); + source->ParamIndices["modelViewProjection"] = 0; + source->ParamIndices["worldToUV0"] = 4; + source->ParamIndices["worldToUV1"] = 5; + source->ParamIndices["refCamDist"] = 6; + source->ParamIndices["distScaleBias"] = 7; + source->ParamIndices["diffuse"] = 8; + source->ParamIndices["blendScale"] = 11; + addSource(source); +} + + +// *************************************************************************** +CVertexProgramDecalAttenuation::~CVertexProgramDecalAttenuation() +{ +} + + +// *************************************************************************** +void CVertexProgramDecalAttenuation::buildInfo() +{ + m_Idx.WorldToUV0 = getUniformIndex("worldToUV0"); + nlassert(m_Idx.WorldToUV0 != std::numeric_limits::max()); + m_Idx.WorldToUV1 = getUniformIndex("worldToUV1"); + nlassert(m_Idx.WorldToUV1 != std::numeric_limits::max()); + m_Idx.RefCamDist = getUniformIndex("refCamDist"); + nlassert(m_Idx.RefCamDist != std::numeric_limits::max()); + m_Idx.DistScaleBias = getUniformIndex("distScaleBias"); + nlassert(m_Idx.DistScaleBias != std::numeric_limits::max()); + m_Idx.Diffuse = getUniformIndex("diffuse"); + nlassert(m_Idx.Diffuse != std::numeric_limits::max()); + m_Idx.BlendScale = getUniformIndex("blendScale"); + nlassert(m_Idx.BlendScale != std::numeric_limits::max()); +} + + // *************************************************************************** CDecalManager::CDecalManager() : _NextMaterialId(0), - _UseVertexProgram(false) + _UseVertexProgram(false), + _DistScale(0.f), + _DistBias(1.f) { - // Fixed-size AGP Volatile vertex buffer with Position + TexCoord0 (see PDF §4.5.4) + // Fixed-size AGP Volatile vertex buffer with Position + TexCoord0 + PrimaryColor (see PDF §4.5.4) _VB.setPreferredMemory(CVertexBuffer::AGPVolatile, true); - _VB.setVertexFormat(CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag); + _VB.setVertexFormat(CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag | CVertexBuffer::PrimaryColorFlag); _VB.setNumVertices(NL3D_DECAL_VB_MAX_VERTICES); + + // Create the vertex program instance + _VertexProgram = new CVertexProgramDecalAttenuation(); } @@ -75,31 +169,52 @@ uint32 CDecalManager::registerMaterial(const CMaterial &mat) } +// *************************************************************************** +// Helper: avoid division by zero for blend scale computation +static inline float decal_favoid0(float f) +{ + return NLMISC::favoid0(f); +} + + // *************************************************************************** // Rendering: implements the pseudocode from PDF §.6.3 // -// for each material { -// count = 0; offset = 0 -// while there are remaining decals { -// length = number of vertices for current decal -// if count + length - offset > array buffer size { -// copy partial, render full buffer, count = 0 -// } else { -// copy remaining, increment decal -// } -// } -// if count > 0 { render remaining } -// } +// With vertex program path: +// - VP handles MVP transform, UV generation, distance attenuation, +// bottom/top Z blending, diffuse color modulation +// - Per-decal VP constants set before each decal's vertices are flushed +// +// Without vertex program (CPU fallback): +// - UVs and per-vertex colors precomputed by CDecal::computeDecal() +// - Written directly into the VB alongside positions void CDecalManager::flush(CScene *sc) { if (_Decals.empty()) return; IDriver *drv = sc->getRenderTrav().getDriver(); - drv->activeVertexProgram(NULL); + + // Try to compile and activate the vertex program + bool vpActive = false; + CVertexProgramDecalAttenuation *vp = _VertexProgram; + if (_UseVertexProgram && vp && drv->compileVertexProgram(vp)) + { + drv->activeVertexProgram(vp); + // Set distance attenuation constants (shared across all decals) + drv->setUniform4f(IDriver::VertexProgram, vp->idx().DistScaleBias, _DistScale, _DistBias, 0.f, 1.f); + vpActive = true; + } + else + { + drv->activeVertexProgram(NULL); + } + drv->activeVertexBuffer(_VB); drv->setupModelMatrix(CMatrix::Identity); + const CVector &camPos = sc->getCam()->getMatrix().getPos(); + // Iterate over each material group TDecalMap::iterator matIt = _Decals.begin(); TDecalMap::iterator matEnd = _Decals.end(); @@ -146,14 +261,53 @@ void CDecalManager::flush(CScene *sc) CDecal *decal = decals[d]; // Get this decal's computed vertices and UVs - std::vector &verts = decal->getVertices(_UseVertexProgram); + std::vector &verts = decal->getVertices(vpActive); const std::vector &uvs = decal->getUVs(); if (verts.empty()) continue; + // If using VP, set per-decal constants before we start copying vertices + if (vpActive) + { + // WorldToUV: rows of the inverse world matrix for UV generation + const CMatrix &worldToUV = decal->getWorldToUVMatrix(); + float row0[4], row1[4]; + // Row 0: maps world position to U coordinate + row0[0] = worldToUV.get()[0]; // column 0, row 0 + row0[1] = worldToUV.get()[4]; // column 1, row 0 + row0[2] = worldToUV.get()[8]; // column 2, row 0 + row0[3] = worldToUV.get()[12]; // column 3, row 0 (translation) + // Row 1: maps world position to V coordinate (inverted Y for UV) + row1[0] = -worldToUV.get()[1]; + row1[1] = -worldToUV.get()[5]; + row1[2] = -worldToUV.get()[9]; + row1[3] = 1.0f - (-worldToUV.get()[13]); // offset for V flip + drv->setUniform4f(IDriver::VertexProgram, vp->idx().WorldToUV0, row0[0], row0[1], row0[2], row0[3]); + drv->setUniform4f(IDriver::VertexProgram, vp->idx().WorldToUV1, row1[0], row1[1], row1[2], row1[3]); + + // Camera position (world space) + drv->setUniform4f(IDriver::VertexProgram, vp->idx().RefCamDist, camPos.x, camPos.y, camPos.z, 1.f); + + // Diffuse color (normalized to [0,1]) + CRGBA diff = decal->getDiffuse(); + drv->setUniform4f(IDriver::VertexProgram, vp->idx().Diffuse, diff.R * (1.f / 255.f), diff.G * (1.f / 255.f), diff.B * (1.f / 255.f), 1.f); + + // Bottom & top blend scale/bias + float bottomBlendScale = 1.f / decal_favoid0(decal->getBottomBlendZMax() - decal->getBottomBlendZMin()); + float topBlendScale = 1.f / decal_favoid0(decal->getTopBlendZMin() - decal->getTopBlendZMax()); + drv->setUniform4f(IDriver::VertexProgram, vp->idx().BlendScale, + bottomBlendScale, -bottomBlendScale * decal->getBottomBlendZMin(), + topBlendScale, -topBlendScale * decal->getTopBlendZMax()); + + // MVP matrix + drv->setUniformMatrix(IDriver::VertexProgram, vp->getUniformIndex(CProgramIndex::ModelViewProjection), IDriver::ModelViewProjection, IDriver::Identity); + } + uint32 length = (uint32)verts.size(); uint32 offset = 0; + const std::vector &colors = decal->getColors(); + bool hasColors = (colors.size() == verts.size()); while (offset < length) { @@ -171,6 +325,14 @@ void CDecalManager::flush(CScene *sc) *vba.getVertexCoordPointer(count + i) = verts[offset + i]; if (offset + i < uvs.size()) *vba.getTexCoordPointer(count + i, 0) = uvs[offset + i]; + if (!vpActive && hasColors) + { + *(CRGBA *)vba.getColorPointer(count + i) = colors[offset + i]; + } + else + { + *(CRGBA *)vba.getColorPointer(count + i) = CRGBA::White; + } } } offset += space; @@ -192,6 +354,14 @@ void CDecalManager::flush(CScene *sc) *vba.getVertexCoordPointer(count + i) = verts[offset + i]; if (offset + i < uvs.size()) *vba.getTexCoordPointer(count + i, 0) = uvs[offset + i]; + if (!vpActive && hasColors) + { + *(CRGBA *)vba.getColorPointer(count + i) = colors[offset + i]; + } + else + { + *(CRGBA *)vba.getColorPointer(count + i) = CRGBA::White; + } } } count += remaining; @@ -207,4 +377,10 @@ void CDecalManager::flush(CScene *sc) drv->renderRawTriangles(*mat, 0, count / 3); } } + + // Deactivate vertex program + if (vpActive) + { + drv->activeVertexProgram(NULL); + } } diff --git a/nel/src/3d/u_decal.cpp b/nel/src/3d/u_decal.cpp index 73867e504a..cd32b8edb2 100644 --- a/nel/src/3d/u_decal.cpp +++ b/nel/src/3d/u_decal.cpp @@ -72,4 +72,32 @@ void UDecal::setStatic(bool isStatic) object->setStatic(isStatic); } +// *************************************************************************** +void UDecal::setDiffuse(NLMISC::CRGBA diffuse) +{ + CDecal *object = getObjectPtr(); + object->setDiffuse(diffuse); +} + +// *************************************************************************** +void UDecal::setEmissive(NLMISC::CRGBA emissive) +{ + CDecal *object = getObjectPtr(); + object->setEmissive(emissive); +} + +// *************************************************************************** +void UDecal::setBottomBlend(float zMin, float zMax) +{ + CDecal *object = getObjectPtr(); + object->setBottomBlend(zMin, zMax); +} + +// *************************************************************************** +void UDecal::setTopBlend(float zMin, float zMax) +{ + CDecal *object = getObjectPtr(); + object->setTopBlend(zMin, zMax); +} + } // NL3D From e362006e20b6b841da6aede43c116072c60bcff2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 21:45:45 +0000 Subject: [PATCH 11/24] =?UTF-8?q?Phase=203:=20Feature=20parity=20=E2=80=94?= =?UTF-8?q?=20priority=20ordering,=20down-facing=20clip,=20custom=20UV=20m?= =?UTF-8?q?atrix,=20helper=20matrices,=20mask=20texture?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/include/nel/3d/decal.h | 60 ++++++++++++ nel/include/nel/3d/decal_manager.h | 2 +- nel/include/nel/3d/u_decal.h | 20 ++++ nel/src/3d/decal.cpp | 136 ++++++++++++++++++++++++--- nel/src/3d/decal_manager.cpp | 13 +++ nel/src/3d/shadow_poly_receiver.cpp | 16 ++++ nel/src/3d/u_decal.cpp | 42 +++++++++ nel/src/3d/visual_collision_mesh.cpp | 10 ++ 8 files changed, 286 insertions(+), 13 deletions(-) diff --git a/nel/include/nel/3d/decal.h b/nel/include/nel/3d/decal.h index 79789766d8..6b1e45be89 100644 --- a/nel/include/nel/3d/decal.h +++ b/nel/include/nel/3d/decal.h @@ -94,6 +94,7 @@ class CDecalContext CMatrix WorldMatrix; std::vector *DestTris; TDecalClipMode ClipMode; + bool ClipDownFacing; }; @@ -203,6 +204,56 @@ class CDecal : public CTransform /// Return whether this decal is marked static. bool isStatic() const { return _IsStatic; } + /** Set the render priority (0 = highest, 7 = lowest). + * Decals with lower priority values are rendered first within their material group. + * \param priority Value in [0, 7] + */ + void setPriority(uint8 priority) { _Priority = (priority < 8) ? priority : 7; } + + /// Get the current render priority. + uint8 getPriority() const { return _Priority; } + + /** Set whether downward-facing surfaces should be clipped. + * When enabled, triangles whose normal has a negative Z component + * are excluded from the decal projection. + * \param clipDownFacing true to clip down-facing surfaces + */ + void setClipDownFacing(bool clipDownFacing) { _ClipDownFacing = clipDownFacing; } + + /// Get whether down-facing clip is enabled. + bool getClipDownFacing() const { return _ClipDownFacing; } + + /** Set a custom UV matrix (world → UV transform). + * When enabled, this matrix replaces the default inverse-world UV generation. + * \param on true to enable, false to return to default UV generation + * \param matrix The world-to-UV matrix (only used when on=true) + */ + void setCustomUVMatrix(bool on, const CMatrix &matrix = CMatrix::Identity); + + /** Set the texture coordinate transform matrix. + * Applied to the UV generation pipeline (multiplied with the inverse-world matrix). + * \param matrix The texture transform matrix + */ + void setTextureMatrix(const CMatrix &matrix); + + /** Set the world matrix for an arrow-shaped decal. + * Convenience method that computes a world matrix for a decal stretched + * from start to end with the given half-width. + * \param start 2D start position + * \param end 2D end position + * \param halfWidth Half-width of the arrow + */ + void setWorldMatrixForArrow(const NLMISC::CVector2f &start, const NLMISC::CVector2f &end, float halfWidth); + + /** Set the world matrix for a spot-shaped decal. + * Convenience method that computes a world matrix for a circular decal + * centered at pos with given radius and optional rotation. + * \param pos 2D center position + * \param radius Radius of the spot + * \param angleInRadians Optional rotation angle + */ + void setWorldMatrixForSpot(const NLMISC::CVector2f &pos, float radius, float angleInRadians = 0.f); + /** Set the diffuse color applied to the decal. * The RGB components tint the texture, alpha is a base opacity. * \param diffuse RGBA diffuse color @@ -299,6 +350,8 @@ class CDecal : public CTransform std::vector _UVs; std::vector _Colors; bool _IsStatic; + uint8 _Priority; + bool _ClipDownFacing; CUV _UV1; CUV _UV2; @@ -312,6 +365,13 @@ class CDecal : public CTransform float _TopBlendZMax; CMatrix _WorldToUVMatrix; + CMatrix _TextureMatrix; + bool _CustomUVMatrixEnabled; + CMatrix _CustomUVMatrix; + + /// Static mask texture for DecalClipMask mode (generated once, shared) + static NLMISC::CSmartPtr _MaskTexture; + static ITexture *getMaskTexture(); }; }//NL3D diff --git a/nel/include/nel/3d/decal_manager.h b/nel/include/nel/3d/decal_manager.h index 863540dff1..460a588497 100644 --- a/nel/include/nel/3d/decal_manager.h +++ b/nel/include/nel/3d/decal_manager.h @@ -151,7 +151,7 @@ class CDecalManager uint32 Id; }; - /// Decals grouped by material ID + /// Decals grouped by material ID, sorted by priority within each group typedef std::map > TDecalMap; TDecalMap _Decals; diff --git a/nel/include/nel/3d/u_decal.h b/nel/include/nel/3d/u_decal.h index e7d6211813..1a9b5e003c 100644 --- a/nel/include/nel/3d/u_decal.h +++ b/nel/include/nel/3d/u_decal.h @@ -27,6 +27,8 @@ #include "nel/misc/types_nl.h" #include "nel/misc/rgba.h" #include "nel/misc/uv.h" +#include "nel/misc/matrix.h" +#include "nel/misc/vector_2f.h" #include "nel/3d/u_transform.h" @@ -77,6 +79,24 @@ class UDecal : public UTransform /// Set the top Z-blend region (fade from 1 at zMin to 0 at zMax) void setTopBlend(float zMin, float zMax); + + /// Set the render priority (0 = highest/first, 7 = lowest/last) + void setPriority(uint8 priority); + + /// Set whether downward-facing surfaces should be clipped + void setClipDownFacing(bool clipDownFacing); + + /// Set a custom UV matrix (world → UV transform), replacing default UV generation + void setCustomUVMatrix(bool on, const NLMISC::CMatrix &matrix = NLMISC::CMatrix::Identity); + + /// Set the texture coordinate transform matrix + void setTextureMatrix(const NLMISC::CMatrix &matrix); + + /// Set the world matrix for an arrow-shaped decal + void setWorldMatrixForArrow(const NLMISC::CVector2f &start, const NLMISC::CVector2f &end, float halfWidth); + + /// Set the world matrix for a spot-shaped decal + void setWorldMatrixForSpot(const NLMISC::CVector2f &pos, float radius, float angleInRadians = 0.f); }; diff --git a/nel/src/3d/decal.cpp b/nel/src/3d/decal.cpp index a7ad992fe2..0dbc131c55 100644 --- a/nel/src/3d/decal.cpp +++ b/nel/src/3d/decal.cpp @@ -27,6 +27,7 @@ #include "nel/3d/decal.h" #include "nel/3d/texture_file.h" +#include "nel/3d/texture_mem.h" #include "nel/3d/scene.h" #include "nel/3d/driver.h" #include "nel/3d/clip_trav.h" @@ -40,7 +41,11 @@ using namespace NLMISC; // *************************************************************************** -CDecalContext::CDecalContext() : ClipMode(DecalClipGeometry), DestTris(NULL) {} +CDecalContext::CDecalContext() : ClipMode(DecalClipGeometry), DestTris(NULL), ClipDownFacing(false) {} + +// *************************************************************************** +// Static mask texture (shared across all decals) +NLMISC::CSmartPtr CDecal::_MaskTexture; // *************************************************************************** @@ -49,6 +54,8 @@ _MaterialId(0), _Touched(true), _StableFrameCount(0), _IsStatic(false), +_Priority(0), +_ClipDownFacing(false), _UV1(CUV(0, 0)), _UV2(CUV(1, 1)), _Diffuse(CRGBA::White), @@ -56,7 +63,8 @@ _Emissive(CRGBA::Black), _BottomBlendZMin(-1e10f), _BottomBlendZMax(-1e10f), _TopBlendZMin(1e10f), -_TopBlendZMax(1e10f) +_TopBlendZMax(1e10f), +_CustomUVMatrixEnabled(false) { setOpacity(true); setTransparency(false); @@ -282,6 +290,7 @@ context.WorldClipPlanes[i].invert(); } context.WorldMatrix = getWorldMatrix(); +context.ClipDownFacing = _ClipDownFacing; // Clear and collect triangles via visual collision (objects/meshes) _Vertices.clear(); @@ -315,6 +324,7 @@ computeColors(0.f, 1.f); // *************************************************************************** // UV coordinate generation (see PDF 4.5.3) // Uses inverse world matrix to project world-space vertices back to unit-cube local space. +// Supports custom UV matrix and texture matrix overrides. // Also builds the worldToUV matrix for the VP path. void CDecal::generateUVs() { @@ -325,19 +335,39 @@ return; // Compute worldToUV matrix: maps world position to [0,1] UV in local decal space CMatrix invWorld = getWorldMatrix().inverted(); -_WorldToUVMatrix = invWorld; -for (uint i = 0; i < _Vertices.size(); ++i) +if (_CustomUVMatrixEnabled) { -// Transform world vertex to local decal space -CVector local = invWorld * _Vertices[i]; - -// Map local X,Y to UV, respecting the UV sub-region -float u = _UV1.U + local.x * (_UV2.U - _UV1.U); -float v = _UV1.V + (1.0f - local.y) * (_UV2.V - _UV1.V); + // Custom UV matrix overrides the entire UV generation pipeline + _WorldToUVMatrix = _CustomUVMatrix; +} +else +{ + // Default: texture matrix × reverse UV matrix × inverse world + CMatrix reverseUV = getReverseUVMatrix(); + _WorldToUVMatrix = _TextureMatrix * reverseUV * invWorld; +} -_UVs[i].U = u; -_UVs[i].V = v; +for (uint i = 0; i < _Vertices.size(); ++i) +{ + float u, v; + if (_CustomUVMatrixEnabled) + { + // Custom matrix outputs UVs directly + CVector uvCoord = _WorldToUVMatrix * _Vertices[i]; + u = uvCoord.x; + v = uvCoord.y; + } + else + { + // Map local X,Y to UV sub-region + CVector local = invWorld * _Vertices[i]; + u = _UV1.U + local.x * (_UV2.U - _UV1.U); + v = _UV1.V + (1.0f - local.y) * (_UV2.V - _UV1.V); + } + + _UVs[i].U = u; + _UVs[i].V = v; } } @@ -392,3 +422,85 @@ _UV1 = uv1; _UV2 = uv2; _Touched = true; } + + +// *************************************************************************** +void CDecal::setCustomUVMatrix(bool on, const CMatrix &matrix) +{ +_CustomUVMatrixEnabled = on; +_CustomUVMatrix = matrix; +_Touched = true; +} + + +// *************************************************************************** +void CDecal::setTextureMatrix(const CMatrix &matrix) +{ +_TextureMatrix = matrix; +_Touched = true; +} + + +// *************************************************************************** +void CDecal::setWorldMatrixForArrow(const NLMISC::CVector2f &start, const NLMISC::CVector2f &end, float halfWidth) +{ +CMatrix matrix; +CVector I = CVector(end.x, end.y, 0.f) - CVector(start.x, start.y, 0.f); +CVector J = 2.f * halfWidth * CVector::K ^ I.normed(); +matrix.setRot(I, J, CVector::K); +matrix.setPos(CVector(start.x, start.y, 0.f) - 0.5f * J); +setMatrix(matrix); +_Touched = true; +} + + +// *************************************************************************** +void CDecal::setWorldMatrixForSpot(const NLMISC::CVector2f &pos, float radius, float angleInRadians) +{ +CMatrix matrix; +matrix.rotateZ(angleInRadians); +matrix.setScale(2.f * radius); +matrix.setPos(CVector(pos.x - radius, pos.y - radius, 0.f)); +setMatrix(matrix); +_Touched = true; +} + + +// *************************************************************************** +ITexture *CDecal::getMaskTexture() +{ +if (_MaskTexture != NULL) + return _MaskTexture; + +// Generate a 4×4 RGBA texture: opaque white in the 2×2 center, transparent black at edges +const uint32 maskSize = 4; +const uint32 maskCenterMin = 1; // inclusive: center region starts at pixel 1 +const uint32 maskCenterMax = 2; // inclusive: center region ends at pixel 2 +uint32 dataSize = maskSize * maskSize * 4; // RGBA +uint8 *data = new uint8[dataSize]; +memset(data, 0, dataSize); +for (uint y = 0; y < maskSize; ++y) +{ + for (uint x = 0; x < maskSize; ++x) + { + uint idx = (y * maskSize + x) * 4; + // Center pixels are opaque white + if (x >= maskCenterMin && x <= maskCenterMax && y >= maskCenterMin && y <= maskCenterMax) + { + data[idx + 0] = 255; // R + data[idx + 1] = 255; // G + data[idx + 2] = 255; // B + data[idx + 3] = 255; // A + } + // Edge pixels are transparent (already 0) + } +} + +CTextureMem *tex = new CTextureMem(data, dataSize, true, false, maskSize, maskSize, CBitmap::RGBA); +tex->setWrapS(ITexture::Clamp); +tex->setWrapT(ITexture::Clamp); +tex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); +tex->setUploadFormat(ITexture::RGBA8888); +_MaskTexture = tex; +return _MaskTexture; +} diff --git a/nel/src/3d/decal_manager.cpp b/nel/src/3d/decal_manager.cpp index 98ec6ebcd5..26f9dea4a4 100644 --- a/nel/src/3d/decal_manager.cpp +++ b/nel/src/3d/decal_manager.cpp @@ -29,11 +29,21 @@ #include "nel/3d/decal_manager.h" #include "nel/3d/scene.h" +#include + using namespace std; using namespace NLMISC; using namespace NL3D; +// *************************************************************************** +// Comparator for sorting decals by priority (lower priority value = rendered first) +static bool decalPriorityCompare(const CDecal *a, const CDecal *b) +{ + return a->getPriority() < b->getPriority(); +} + + // *************************************************************************** // Vertex program assembly code for decal attenuation (ported from legacy system) // Constants: @@ -227,6 +237,9 @@ void CDecalManager::flush(CScene *sc) if (decals.empty()) continue; + // Sort decals by priority within this material group (lower priority first) + std::sort(decals.begin(), decals.end(), decalPriorityCompare); + // Find the registered material for this ID CMaterial *mat = NULL; for (uint m = 0; m < _Materials.size(); ++m) diff --git a/nel/src/3d/shadow_poly_receiver.cpp b/nel/src/3d/shadow_poly_receiver.cpp index 8d370c3c22..db3601e061 100644 --- a/nel/src/3d/shadow_poly_receiver.cpp +++ b/nel/src/3d/shadow_poly_receiver.cpp @@ -613,6 +613,14 @@ void CShadowPolyReceiver::receiveDecal(CDecalContext &cdc, const CVector &vertDe for (uint triIndex = 0; triIndex < visibleTris.size(); ++triIndex) { CTriangleId &triId = *visibleTris[triIndex]; + + // Skip down-facing triangles if requested + if (cdc.ClipDownFacing) + { + CVector triNormal = (_Vertices[triId.Vertex[1]] - _Vertices[triId.Vertex[0]]) ^ (_Vertices[triId.Vertex[2]] - _Vertices[triId.Vertex[0]]); + if (triNormal.z < 0.f) continue; + } + clippedTri.Vertices.resize(3); clippedTri.Vertices[0] = _Vertices[triId.Vertex[0]]; clippedTri.Vertices[1] = _Vertices[triId.Vertex[1]]; @@ -635,6 +643,14 @@ void CShadowPolyReceiver::receiveDecal(CDecalContext &cdc, const CVector &vertDe for (uint triIndex = 0; triIndex < visibleTris.size(); ++triIndex) { CTriangleId &triId = *visibleTris[triIndex]; + + // Skip down-facing triangles if requested + if (cdc.ClipDownFacing) + { + CVector triNormal = (_Vertices[triId.Vertex[1]] - _Vertices[triId.Vertex[0]]) ^ (_Vertices[triId.Vertex[2]] - _Vertices[triId.Vertex[0]]); + if (triNormal.z < 0.f) continue; + } + cdc.DestTris->push_back(_Vertices[triId.Vertex[0]] + vertDelta); cdc.DestTris->push_back(_Vertices[triId.Vertex[1]] + vertDelta); cdc.DestTris->push_back(_Vertices[triId.Vertex[2]] + vertDelta); diff --git a/nel/src/3d/u_decal.cpp b/nel/src/3d/u_decal.cpp index cd32b8edb2..0bb7f49ba1 100644 --- a/nel/src/3d/u_decal.cpp +++ b/nel/src/3d/u_decal.cpp @@ -100,4 +100,46 @@ void UDecal::setTopBlend(float zMin, float zMax) object->setTopBlend(zMin, zMax); } +// *************************************************************************** +void UDecal::setPriority(uint8 priority) +{ + CDecal *object = getObjectPtr(); + object->setPriority(priority); +} + +// *************************************************************************** +void UDecal::setClipDownFacing(bool clipDownFacing) +{ + CDecal *object = getObjectPtr(); + object->setClipDownFacing(clipDownFacing); +} + +// *************************************************************************** +void UDecal::setCustomUVMatrix(bool on, const NLMISC::CMatrix &matrix) +{ + CDecal *object = getObjectPtr(); + object->setCustomUVMatrix(on, matrix); +} + +// *************************************************************************** +void UDecal::setTextureMatrix(const NLMISC::CMatrix &matrix) +{ + CDecal *object = getObjectPtr(); + object->setTextureMatrix(matrix); +} + +// *************************************************************************** +void UDecal::setWorldMatrixForArrow(const NLMISC::CVector2f &start, const NLMISC::CVector2f &end, float halfWidth) +{ + CDecal *object = getObjectPtr(); + object->setWorldMatrixForArrow(start, end, halfWidth); +} + +// *************************************************************************** +void UDecal::setWorldMatrixForSpot(const NLMISC::CVector2f &pos, float radius, float angleInRadians) +{ + CDecal *object = getObjectPtr(); + object->setWorldMatrixForSpot(pos, radius, angleInRadians); +} + } // NL3D diff --git a/nel/src/3d/visual_collision_mesh.cpp b/nel/src/3d/visual_collision_mesh.cpp index c49750ba35..59a59d57c5 100644 --- a/nel/src/3d/visual_collision_mesh.cpp +++ b/nel/src/3d/visual_collision_mesh.cpp @@ -446,6 +446,16 @@ void CVisualCollisionMesh::receiveDecal(const NLMISC::CMatrix &instanceMatrix, // if triangle not clipped, add the triangle if( (triFlag & NL3D_VCM_SHADOW_NUM_CLIP_PLANE_MASK)==0 ) { + // Skip down-facing triangles if requested + if (decalContext.ClipDownFacing) + { + CVector v0 = instanceMatrix * _Vertices[triId[0]]; + CVector v1 = instanceMatrix * _Vertices[triId[1]]; + CVector v2 = instanceMatrix * _Vertices[triId[2]]; + CVector normal = (v1 - v0) ^ (v2 - v0); + if (normal.z < 0.f) continue; + } + if(decalContext.ClipMode == DecalClipGeometry) { // Geometry clipping: clip vertices against bounding box planes (saves fillrate) From 00eb07242b7a7b8effe74caed8de50249f5f07fe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 22:17:09 +0000 Subject: [PATCH 12/24] Phase 4: Optimize VB lock granularity, fix first-frame bug, add mipmap limiting for atlas textures Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/include/nel/3d/decal.h | 1 + nel/src/3d/decal.cpp | 31 ++++++++++++++++ nel/src/3d/decal_manager.cpp | 70 ++++++++++++++++++++---------------- 3 files changed, 72 insertions(+), 30 deletions(-) diff --git a/nel/include/nel/3d/decal.h b/nel/include/nel/3d/decal.h index 6b1e45be89..e8fe7d53a5 100644 --- a/nel/include/nel/3d/decal.h +++ b/nel/include/nel/3d/decal.h @@ -341,6 +341,7 @@ class CDecal : public CTransform uint32 _MaterialId; bool _Touched; + bool _FirstFrame; ///< True until the first frame has been traversed (matrices invalid on frame 0, see PDF §4.6.4) uint32 _StableFrameCount; CVector _LastCamPos; diff --git a/nel/src/3d/decal.cpp b/nel/src/3d/decal.cpp index 0dbc131c55..4059988673 100644 --- a/nel/src/3d/decal.cpp +++ b/nel/src/3d/decal.cpp @@ -52,6 +52,7 @@ NLMISC::CSmartPtr CDecal::_MaskTexture; CDecal::CDecal() : _MaterialId(0), _Touched(true), +_FirstFrame(true), _StableFrameCount(0), _IsStatic(false), _Priority(0), @@ -212,6 +213,19 @@ std::vector &CDecal::getVertices(const bool useVertexProgram) { const NLMISC::CVector &camPos = getOwnerScene()->getCam()->getMatrix().getPos(); +// First-frame skip: matrices are incorrect on the first traversal (PDF §4.6.4). +// Return empty vertices and defer computation to the next frame. +if (_FirstFrame) +{ + _FirstFrame = false; + _LastCamPos = camPos; + _Touched = true; + _Vertices.clear(); + _UVs.clear(); + _Colors.clear(); + return _Vertices; +} + if (_IsStatic) { // Static decal: only recompute on first touch. @@ -421,6 +435,23 @@ void CDecal::setUVCoord(const CUV uv1, const CUV uv2) _UV1 = uv1; _UV2 = uv2; _Touched = true; + +// Mipmap limiting for texture atlases (PDF §4.6.2): +// When using a sub-region, mipmaps can bleed into neighboring portions. +// Disable mipmaps on the texture to prevent this. +ITexture *tex = _Mat.getTexture(0); +if (tex) +{ + bool isSubRegion = (fabsf(uv1.U) > 1e-6f || fabsf(uv1.V) > 1e-6f || fabsf(uv2.U - 1.f) > 1e-6f || fabsf(uv2.V - 1.f) > 1e-6f); + if (isSubRegion) + { + tex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapOff); + } + else + { + tex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapLinear); + } +} } diff --git a/nel/src/3d/decal_manager.cpp b/nel/src/3d/decal_manager.cpp index 26f9dea4a4..8244ff6f9b 100644 --- a/nel/src/3d/decal_manager.cpp +++ b/nel/src/3d/decal_manager.cpp @@ -268,6 +268,8 @@ void CDecalManager::flush(CScene *sc) } uint32 count = 0; // current position in VB + bool vbLocked = false; + CVertexBufferReadWrite vba; for (uint d = 0; d < decals.size(); ++d) { @@ -322,6 +324,13 @@ void CDecalManager::flush(CScene *sc) const std::vector &colors = decal->getColors(); bool hasColors = (colors.size() == verts.size()); + // Ensure VB is locked before copying + if (!vbLocked) + { + _VB.lock(vba); + vbLocked = true; + } + while (offset < length) { uint32 remaining = length - offset; @@ -330,51 +339,47 @@ void CDecalManager::flush(CScene *sc) if (remaining > space) { // Not enough space: fill what we can, render, reset + for (uint32 i = 0; i < space; ++i) { - CVertexBufferReadWrite vba; - _VB.lock(vba); - for (uint32 i = 0; i < space; ++i) + *vba.getVertexCoordPointer(count + i) = verts[offset + i]; + if (offset + i < uvs.size()) + *vba.getTexCoordPointer(count + i, 0) = uvs[offset + i]; + if (!vpActive && hasColors) + { + *(CRGBA *)vba.getColorPointer(count + i) = colors[offset + i]; + } + else { - *vba.getVertexCoordPointer(count + i) = verts[offset + i]; - if (offset + i < uvs.size()) - *vba.getTexCoordPointer(count + i, 0) = uvs[offset + i]; - if (!vpActive && hasColors) - { - *(CRGBA *)vba.getColorPointer(count + i) = colors[offset + i]; - } - else - { - *(CRGBA *)vba.getColorPointer(count + i) = CRGBA::White; - } + *(CRGBA *)vba.getColorPointer(count + i) = CRGBA::White; } } offset += space; count += space; - // Render the full buffer + // Unlock, render, re-lock + vba.unlock(); + vbLocked = false; nlassert(count % 3 == 0); drv->renderRawTriangles(*mat, 0, count / 3); count = 0; + _VB.lock(vba); + vbLocked = true; } else { // Enough space: copy all remaining + for (uint32 i = 0; i < remaining; ++i) { - CVertexBufferReadWrite vba; - _VB.lock(vba); - for (uint32 i = 0; i < remaining; ++i) + *vba.getVertexCoordPointer(count + i) = verts[offset + i]; + if (offset + i < uvs.size()) + *vba.getTexCoordPointer(count + i, 0) = uvs[offset + i]; + if (!vpActive && hasColors) { - *vba.getVertexCoordPointer(count + i) = verts[offset + i]; - if (offset + i < uvs.size()) - *vba.getTexCoordPointer(count + i, 0) = uvs[offset + i]; - if (!vpActive && hasColors) - { - *(CRGBA *)vba.getColorPointer(count + i) = colors[offset + i]; - } - else - { - *(CRGBA *)vba.getColorPointer(count + i) = CRGBA::White; - } + *(CRGBA *)vba.getColorPointer(count + i) = colors[offset + i]; + } + else + { + *(CRGBA *)vba.getColorPointer(count + i) = CRGBA::White; } } count += remaining; @@ -383,7 +388,12 @@ void CDecalManager::flush(CScene *sc) } } - // Render any remaining vertices for this material + // Unlock and render any remaining vertices for this material + if (vbLocked) + { + vba.unlock(); + vbLocked = false; + } if (count > 0) { nlassert(count % 3 == 0); From 698ba8144d2e21a4b54fa9ea75c9ce501fed5057 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 22:33:14 +0000 Subject: [PATCH 13/24] Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- .../lua_ide_dll_nevrax/source/libapi/libapi.c | 294 ++-- .../source/standard/lauxlib.c | 1202 ++++++------- .../source/standard/lbaselib.c | 1368 +++++++-------- .../source/standard/ldblib.c | 618 +++---- .../source/standard/liolib.c | 1520 ++++++++-------- .../source/standard/lmathlib.c | 512 +++--- .../source/standard/loadlib.c | 410 ++--- .../source/standard/lstrlib.c | 1560 ++++++++--------- .../source/standard/ltablib.c | 520 +++--- .../src/ai_service/doc/doxycfg/doxygen.css | 124 +- .../sadge_lib/include/text_input.h | 2 +- .../sadge_lib/include/text_output.h | 2 +- 12 files changed, 4066 insertions(+), 4066 deletions(-) diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/libapi/libapi.c b/ryzom/client/src/lua_ide_dll_nevrax/source/libapi/libapi.c index ddc5c620e3..5f2a007d46 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/libapi/libapi.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/libapi/libapi.c @@ -1,147 +1,147 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#include "stdafx.h" - -#include "../ide2/DebuggerMessages.h" - -static HWND g_hWnd; - -using namespace std; - -// maximum mumber of lines the output console should have -static const WORD MAX_CONSOLE_LINES = 500; - -BOOL isWindowsNT(void) -{ - static BOOL once = FALSE; - static BOOL isNT = FALSE; - - if (!once) - { - OSVERSIONINFO osver; - osver.dwOSVersionInfoSize = sizeof(osver); - if (GetVersionEx(&osver)) - if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) - isNT = TRUE; - once = TRUE; - } - return isNT; -} - -static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd) -{ - char tmp[20], *tty; - if (isWindowsNT()) - tty = "ConsoleWindowClass"; - else - tty = "tty"; - if (GetClassName(wnd, tmp, sizeof(tmp)) && !strcmp(tmp, tty)) - { - DWORD wndproc, thisproc = GetCurrentProcessId(); - GetWindowThreadProcessId(wnd, &wndproc); - if (wndproc == thisproc) { - *((HWND*)retwnd) = wnd; - return FALSE; - } - } - return TRUE; -} - - -static BOOL CtrlHandler(DWORD fdwCtrlType) -{ - switch (fdwCtrlType) - { - // Handle the CTRL+C signal. - - case CTRL_C_EVENT: - case CTRL_BREAK_EVENT: - return TRUE; - - // CTRL+CLOSE: confirm that the user wants to exit. - - case CTRL_CLOSE_EVENT: - - case CTRL_LOGOFF_EVENT: - - case CTRL_SHUTDOWN_EVENT: - - default: - - return FALSE; - } -} - -void AttachConsole() -{ - int hConHandle; - long lStdHandle; - CONSOLE_SCREEN_BUFFER_INFO coninfo; - FILE *fp; - HWND hWndConsole; - HMENU hSysMenu; - -// allocate a console for this app - FreeConsole(); - AllocConsole(); - -//disable system menu -> close box - EnumWindows(EnumttyWindow, (long)(&hWndConsole)); - hSysMenu = GetSystemMenu(hWndConsole, FALSE); - ModifyMenu(hSysMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED, 0, 0); - ShowWindow(hWndConsole, SW_SHOW); - -// set the screen buffer to be big enough to let us scroll text - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); - coninfo.dwSize.Y = MAX_CONSOLE_LINES; - SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); - -// redirect unbuffered STDOUT to the console - lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE); - hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); - fp = _fdopen( hConHandle, "w" ); - *stdout = *fp; - setvbuf( stdout, NULL, _IONBF, 0 ); - -// redirect unbuffered STDIN to the console - lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE); - hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); - ::SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_MOUSE_INPUT); - fp = _fdopen( hConHandle, "r" ); - *stdin = *fp; - setvbuf( stdin, NULL, _IONBF, 0 ); - -// redirect unbuffered STDERR to the console - lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE); - hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); - fp = _fdopen( hConHandle, "w" ); - *stderr = *fp; - setvbuf( stderr, NULL, _IONBF, 0 ); - -// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog -// point to console as well - ios::sync_with_stdio(); - - SetConsoleCtrlHandler( - (PHANDLER_ROUTINE) CtrlHandler, // handler function - TRUE); // add to list -} - -BOOL InitLibAPI(HWND hWnd) -{ - g_hWnd = hWnd; -} +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "stdafx.h" + +#include "../ide2/DebuggerMessages.h" + +static HWND g_hWnd; + +using namespace std; + +// maximum mumber of lines the output console should have +static const WORD MAX_CONSOLE_LINES = 500; + +BOOL isWindowsNT(void) +{ + static BOOL once = FALSE; + static BOOL isNT = FALSE; + + if (!once) + { + OSVERSIONINFO osver; + osver.dwOSVersionInfoSize = sizeof(osver); + if (GetVersionEx(&osver)) + if (osver.dwPlatformId == VER_PLATFORM_WIN32_NT) + isNT = TRUE; + once = TRUE; + } + return isNT; +} + +static BOOL CALLBACK EnumttyWindow(HWND wnd, LPARAM retwnd) +{ + char tmp[20], *tty; + if (isWindowsNT()) + tty = "ConsoleWindowClass"; + else + tty = "tty"; + if (GetClassName(wnd, tmp, sizeof(tmp)) && !strcmp(tmp, tty)) + { + DWORD wndproc, thisproc = GetCurrentProcessId(); + GetWindowThreadProcessId(wnd, &wndproc); + if (wndproc == thisproc) { + *((HWND*)retwnd) = wnd; + return FALSE; + } + } + return TRUE; +} + + +static BOOL CtrlHandler(DWORD fdwCtrlType) +{ + switch (fdwCtrlType) + { + // Handle the CTRL+C signal. + + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + return TRUE; + + // CTRL+CLOSE: confirm that the user wants to exit. + + case CTRL_CLOSE_EVENT: + + case CTRL_LOGOFF_EVENT: + + case CTRL_SHUTDOWN_EVENT: + + default: + + return FALSE; + } +} + +void AttachConsole() +{ + int hConHandle; + long lStdHandle; + CONSOLE_SCREEN_BUFFER_INFO coninfo; + FILE *fp; + HWND hWndConsole; + HMENU hSysMenu; + +// allocate a console for this app + FreeConsole(); + AllocConsole(); + +//disable system menu -> close box + EnumWindows(EnumttyWindow, (long)(&hWndConsole)); + hSysMenu = GetSystemMenu(hWndConsole, FALSE); + ModifyMenu(hSysMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED, 0, 0); + ShowWindow(hWndConsole, SW_SHOW); + +// set the screen buffer to be big enough to let us scroll text + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo); + coninfo.dwSize.Y = MAX_CONSOLE_LINES; + SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize); + +// redirect unbuffered STDOUT to the console + lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE); + hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + fp = _fdopen( hConHandle, "w" ); + *stdout = *fp; + setvbuf( stdout, NULL, _IONBF, 0 ); + +// redirect unbuffered STDIN to the console + lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE); + hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + ::SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_MOUSE_INPUT); + fp = _fdopen( hConHandle, "r" ); + *stdin = *fp; + setvbuf( stdin, NULL, _IONBF, 0 ); + +// redirect unbuffered STDERR to the console + lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE); + hConHandle = _open_osfhandle(lStdHandle, _O_TEXT); + fp = _fdopen( hConHandle, "w" ); + *stderr = *fp; + setvbuf( stderr, NULL, _IONBF, 0 ); + +// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog +// point to console as well + ios::sync_with_stdio(); + + SetConsoleCtrlHandler( + (PHANDLER_ROUTINE) CtrlHandler, // handler function + TRUE); // add to list +} + +BOOL InitLibAPI(HWND hWnd) +{ + g_hWnd = hWnd; +} diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lauxlib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lauxlib.c index adc01cb898..1ef9877354 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lauxlib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lauxlib.c @@ -1,601 +1,601 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include -#include -#include -#include -#include - - -/* This file uses only the official API of Lua. -** Any function declared here could be written as an application function. -*/ - -#define lauxlib_c - -#include "lua.h" - -#include "lauxlib.h" - - -/* number of prereserved references (for internal use) */ -#define RESERVED_REFS 2 - -/* reserved references */ -#define FREELIST_REF 1 /* free list of references */ -#define ARRAYSIZE_REF 2 /* array sizes */ - - -/* convert a stack index to positive */ -#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \ - lua_gettop(L) + (i) + 1) - - -/* -** {====================================================== -** Error-report functions -** ======================================================= -*/ - - -LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) { - lua_Debug ar; - lua_getstack(L, 0, &ar); - lua_getinfo(L, "n", &ar); - if (strcmp(ar.namewhat, "method") == 0) { - narg--; /* do not count `self' */ - if (narg == 0) /* error is in the self argument itself? */ - return luaL_error(L, "calling `%s' on bad self (%s)", ar.name, extramsg); - } - if (ar.name == NULL) - ar.name = "?"; - return luaL_error(L, "bad argument #%d to `%s' (%s)", - narg, ar.name, extramsg); -} - - -LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) { - const char *msg = lua_pushfstring(L, "%s expected, got %s", - tname, lua_typename(L, lua_type(L,narg))); - return luaL_argerror(L, narg, msg); -} - - -static void tag_error (lua_State *L, int narg, int tag) { - luaL_typerror(L, narg, lua_typename(L, tag)); -} - - -LUALIB_API void luaL_where (lua_State *L, int level) { - lua_Debug ar; - if (lua_getstack(L, level, &ar)) { /* check function at level */ - lua_getinfo(L, "Snl", &ar); /* get info about it */ - if (ar.currentline > 0) { /* is there info? */ - lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); - return; - } - } - lua_pushliteral(L, ""); /* else, no information available... */ -} - - -LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { - va_list argp; - va_start(argp, fmt); - luaL_where(L, 1); - lua_pushvfstring(L, fmt, argp); - va_end(argp); - lua_concat(L, 2); - return lua_error(L); -} - -/* }====================================================== */ - - -LUALIB_API int luaL_findstring (const char *name, const char *const list[]) { - int i; - for (i=0; list[i]; i++) - if (strcmp(list[i], name) == 0) - return i; - return -1; /* name not found */ -} - - -LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { - lua_pushstring(L, tname); - lua_rawget(L, LUA_REGISTRYINDEX); /* get registry.name */ - if (!lua_isnil(L, -1)) /* name already in use? */ - return 0; /* leave previous value on top, but return 0 */ - lua_pop(L, 1); - lua_newtable(L); /* create metatable */ - lua_pushstring(L, tname); - lua_pushvalue(L, -2); - lua_rawset(L, LUA_REGISTRYINDEX); /* registry.name = metatable */ - lua_pushvalue(L, -1); - lua_pushstring(L, tname); - lua_rawset(L, LUA_REGISTRYINDEX); /* registry[metatable] = name */ - return 1; -} - - -LUALIB_API void luaL_getmetatable (lua_State *L, const char *tname) { - lua_pushstring(L, tname); - lua_rawget(L, LUA_REGISTRYINDEX); -} - - -LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { - const char *tn; - if (!lua_getmetatable(L, ud)) return NULL; /* no metatable? */ - lua_rawget(L, LUA_REGISTRYINDEX); /* get registry[metatable] */ - tn = lua_tostring(L, -1); - if (tn && (strcmp(tn, tname) == 0)) { - lua_pop(L, 1); - return lua_touserdata(L, ud); - } - else { - lua_pop(L, 1); - return NULL; - } -} - - -LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) { - if (!lua_checkstack(L, space)) - luaL_error(L, "stack overflow (%s)", mes); -} - - -LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) { - if (lua_type(L, narg) != t) - tag_error(L, narg, t); -} - - -LUALIB_API void luaL_checkany (lua_State *L, int narg) { - if (lua_type(L, narg) == LUA_TNONE) - luaL_argerror(L, narg, "value expected"); -} - - -LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) { - const char *s = lua_tostring(L, narg); - if (!s) tag_error(L, narg, LUA_TSTRING); - if (len) *len = lua_strlen(L, narg); - return s; -} - - -LUALIB_API const char *luaL_optlstring (lua_State *L, int narg, - const char *def, size_t *len) { - if (lua_isnoneornil(L, narg)) { - if (len) - *len = (def ? strlen(def) : 0); - return def; - } - else return luaL_checklstring(L, narg, len); -} - - -LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) { - lua_Number d = lua_tonumber(L, narg); - if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ - tag_error(L, narg, LUA_TNUMBER); - return d; -} - - -LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) { - if (lua_isnoneornil(L, narg)) return def; - else return luaL_checknumber(L, narg); -} - - -LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { - if (!lua_getmetatable(L, obj)) /* no metatable? */ - return 0; - lua_pushstring(L, event); - lua_rawget(L, -2); - if (lua_isnil(L, -1)) { - lua_pop(L, 2); /* remove metatable and metafield */ - return 0; - } - else { - lua_remove(L, -2); /* remove only metatable */ - return 1; - } -} - - -LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { - obj = abs_index(L, obj); - if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ - return 0; - lua_pushvalue(L, obj); - lua_call(L, 1, 1); - return 1; -} - - -LUALIB_API void luaL_openlib (lua_State *L, const char *libname, - const luaL_reg *l, int nup) { - if (libname) { - lua_pushstring(L, libname); - lua_gettable(L, LUA_GLOBALSINDEX); /* check whether lib already exists */ - if (lua_isnil(L, -1)) { /* no? */ - lua_pop(L, 1); - lua_newtable(L); /* create it */ - lua_pushstring(L, libname); - lua_pushvalue(L, -2); - lua_settable(L, LUA_GLOBALSINDEX); /* register it with given name */ - } - lua_insert(L, -(nup+1)); /* move library table to below upvalues */ - } - for (; l->name; l++) { - int i; - lua_pushstring(L, l->name); - for (i=0; ifunc, nup); - lua_settable(L, -(nup+3)); - } - lua_pop(L, nup); /* remove upvalues */ -} - - - -/* -** {====================================================== -** getn-setn: size for arrays -** ======================================================= -*/ - -static int checkint (lua_State *L, int topop) { - int n = (int)lua_tonumber(L, -1); - if (n == 0 && !lua_isnumber(L, -1)) n = -1; - lua_pop(L, topop); - return n; -} - - -static void getsizes (lua_State *L) { - lua_rawgeti(L, LUA_REGISTRYINDEX, ARRAYSIZE_REF); - if (lua_isnil(L, -1)) { /* no `size' table? */ - lua_pop(L, 1); /* remove nil */ - lua_newtable(L); /* create it */ - lua_pushvalue(L, -1); /* `size' will be its own metatable */ - lua_setmetatable(L, -2); - lua_pushliteral(L, "__mode"); - lua_pushliteral(L, "k"); - lua_rawset(L, -3); /* metatable(N).__mode = "k" */ - lua_pushvalue(L, -1); - lua_rawseti(L, LUA_REGISTRYINDEX, ARRAYSIZE_REF); /* store in register */ - } -} - - -void luaL_setn (lua_State *L, int t, int n) { - t = abs_index(L, t); - lua_pushliteral(L, "n"); - lua_rawget(L, t); - if (checkint(L, 1) >= 0) { /* is there a numeric field `n'? */ - lua_pushliteral(L, "n"); /* use it */ - lua_pushnumber(L, (lua_Number)n); - lua_rawset(L, t); - } - else { /* use `sizes' */ - getsizes(L); - lua_pushvalue(L, t); - lua_pushnumber(L, (lua_Number)n); - lua_rawset(L, -3); /* sizes[t] = n */ - lua_pop(L, 1); /* remove `sizes' */ - } -} - - -int luaL_getn (lua_State *L, int t) { - int n; - t = abs_index(L, t); - lua_pushliteral(L, "n"); /* try t.n */ - lua_rawget(L, t); - if ((n = checkint(L, 1)) >= 0) return n; - getsizes(L); /* else try sizes[t] */ - lua_pushvalue(L, t); - lua_rawget(L, -2); - if ((n = checkint(L, 2)) >= 0) return n; - for (n = 1; ; n++) { /* else must count elements */ - lua_rawgeti(L, t, n); - if (lua_isnil(L, -1)) break; - lua_pop(L, 1); - } - lua_pop(L, 1); - return n - 1; -} - -/* }====================================================== */ - - - -/* -** {====================================================== -** Generic Buffer manipulation -** ======================================================= -*/ - - -#define bufflen(B) ((B)->p - (B)->buffer) -#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B))) - -#define LIMIT (LUA_MINSTACK/2) - - -static int emptybuffer (luaL_Buffer *B) { - size_t l = bufflen(B); - if (l == 0) return 0; /* put nothing on stack */ - else { - lua_pushlstring(B->L, B->buffer, l); - B->p = B->buffer; - B->lvl++; - return 1; - } -} - - -static void adjuststack (luaL_Buffer *B) { - if (B->lvl > 1) { - lua_State *L = B->L; - int toget = 1; /* number of levels to concat */ - size_t toplen = lua_strlen(L, -1); - do { - size_t l = lua_strlen(L, -(toget+1)); - if (B->lvl - toget + 1 >= LIMIT || toplen > l) { - toplen += l; - toget++; - } - else break; - } while (toget < B->lvl); - lua_concat(L, toget); - B->lvl = B->lvl - toget + 1; - } -} - - -LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) { - if (emptybuffer(B)) - adjuststack(B); - return B->buffer; -} - - -LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { - while (l--) - luaL_putchar(B, *s++); -} - - -LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { - luaL_addlstring(B, s, strlen(s)); -} - - -LUALIB_API void luaL_pushresult (luaL_Buffer *B) { - emptybuffer(B); - lua_concat(B->L, B->lvl); - B->lvl = 1; -} - - -LUALIB_API void luaL_addvalue (luaL_Buffer *B) { - lua_State *L = B->L; - size_t vl = lua_strlen(L, -1); - if (vl <= bufffree(B)) { /* fit into buffer? */ - memcpy(B->p, lua_tostring(L, -1), vl); /* put it there */ - B->p += vl; - lua_pop(L, 1); /* remove from stack */ - } - else { - if (emptybuffer(B)) - lua_insert(L, -2); /* put buffer before new value */ - B->lvl++; /* add new value into B stack */ - adjuststack(B); - } -} - - -LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { - B->L = L; - B->p = B->buffer; - B->lvl = 0; -} - -/* }====================================================== */ - - -LUALIB_API int luaL_ref (lua_State *L, int t) { - int ref; - t = abs_index(L, t); - if (lua_isnil(L, -1)) { - lua_pop(L, 1); /* remove from stack */ - return LUA_REFNIL; /* `nil' has a unique fixed reference */ - } - lua_rawgeti(L, t, FREELIST_REF); /* get first free element */ - ref = (int)lua_tonumber(L, -1); /* ref = t[FREELIST_REF] */ - lua_pop(L, 1); /* remove it from stack */ - if (ref != 0) { /* any free element? */ - lua_rawgeti(L, t, ref); /* remove it from list */ - lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */ - } - else { /* no free elements */ - ref = luaL_getn(L, t); - if (ref < RESERVED_REFS) - ref = RESERVED_REFS; /* skip reserved references */ - ref++; /* create new reference */ - luaL_setn(L, t, ref); - } - lua_rawseti(L, t, ref); - return ref; -} - - -LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { - if (ref >= 0) { - t = abs_index(L, t); - lua_rawgeti(L, t, FREELIST_REF); - lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */ - lua_pushnumber(L, (lua_Number)ref); - lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */ - } -} - - - -/* -** {====================================================== -** Load functions -** ======================================================= -*/ - -typedef struct LoadF { - FILE *f; - char buff[LUAL_BUFFERSIZE]; -} LoadF; - - -static const char *getF (lua_State *L, void *ud, size_t *size) { - LoadF *lf = (LoadF *)ud; - (void)L; - if (feof(lf->f)) return NULL; - *size = fread(lf->buff, 1, LUAL_BUFFERSIZE, lf->f); - return (*size > 0) ? lf->buff : NULL; -} - - -static int errfile (lua_State *L, int fnameindex) { - const char *filename = lua_tostring(L, fnameindex) + 1; - lua_pushfstring(L, "cannot read %s: %s", filename, strerror(errno)); - lua_remove(L, fnameindex); - return LUA_ERRFILE; -} - - -LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) { - LoadF lf; - int status, readstatus; - int c; - int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ - if (filename == NULL) { - lua_pushliteral(L, "=stdin"); - lf.f = stdin; - } - else { - lua_pushfstring(L, "@%s", filename); - lf.f = fopen(filename, "r"); - } - if (lf.f == NULL) return errfile(L, fnameindex); /* unable to open file */ - c = ungetc(getc(lf.f), lf.f); - if (!(isspace(c) || isprint(c)) && lf.f != stdin) { /* binary file? */ - fclose(lf.f); - lf.f = fopen(filename, "rb"); /* reopen in binary mode */ - if (lf.f == NULL) return errfile(L, fnameindex); /* unable to reopen file */ - } - status = lua_load(L, getF, &lf, lua_tostring(L, -1)); - readstatus = ferror(lf.f); - if (lf.f != stdin) fclose(lf.f); /* close file (even in case of errors) */ - if (readstatus) { - lua_settop(L, fnameindex); /* ignore results from `lua_load' */ - return errfile(L, fnameindex); - } - lua_remove(L, fnameindex); - return status; -} - - -typedef struct LoadS { - const char *s; - size_t size; -} LoadS; - - -static const char *getS (lua_State *L, void *ud, size_t *size) { - LoadS *ls = (LoadS *)ud; - (void)L; - if (ls->size == 0) return NULL; - *size = ls->size; - ls->size = 0; - return ls->s; -} - - -LUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size, - const char *name) { - LoadS ls; - ls.s = buff; - ls.size = size; - return lua_load(L, getS, &ls, name); -} - -/* }====================================================== */ - - -/* -** {====================================================== -** compatibility code -** ======================================================= -*/ - - -static void callalert (lua_State *L, int status) { - if (status != 0) { - lua_getglobal(L, "_ALERT"); - if (lua_isfunction(L, -1)) { - lua_insert(L, -2); - lua_call(L, 1, 0); - } - else { /* no _ALERT function; print it on stderr */ - fprintf(stderr, "%s\n", lua_tostring(L, -2)); - lua_pop(L, 2); /* remove error message and _ALERT */ - } - } -} - - -static int aux_do (lua_State *L, int status) { - if (status == 0) { /* parse OK? */ - status = lua_pcall(L, 0, LUA_MULTRET, 0); /* call main */ - } - callalert(L, status); - return status; -} - - -LUALIB_API int lua_dofile (lua_State *L, const char *filename) { - return aux_do(L, luaL_loadfile(L, filename)); -} - - -LUALIB_API int lua_dobuffer (lua_State *L, const char *buff, size_t size, - const char *name) { - return aux_do(L, luaL_loadbuffer(L, buff, size, name)); -} - - -LUALIB_API int lua_dostring (lua_State *L, const char *str) { - return lua_dobuffer(L, str, strlen(str), str); -} - -/* }====================================================== */ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include +#include +#include +#include +#include + + +/* This file uses only the official API of Lua. +** Any function declared here could be written as an application function. +*/ + +#define lauxlib_c + +#include "lua.h" + +#include "lauxlib.h" + + +/* number of prereserved references (for internal use) */ +#define RESERVED_REFS 2 + +/* reserved references */ +#define FREELIST_REF 1 /* free list of references */ +#define ARRAYSIZE_REF 2 /* array sizes */ + + +/* convert a stack index to positive */ +#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \ + lua_gettop(L) + (i) + 1) + + +/* +** {====================================================== +** Error-report functions +** ======================================================= +*/ + + +LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) { + lua_Debug ar; + lua_getstack(L, 0, &ar); + lua_getinfo(L, "n", &ar); + if (strcmp(ar.namewhat, "method") == 0) { + narg--; /* do not count `self' */ + if (narg == 0) /* error is in the self argument itself? */ + return luaL_error(L, "calling `%s' on bad self (%s)", ar.name, extramsg); + } + if (ar.name == NULL) + ar.name = "?"; + return luaL_error(L, "bad argument #%d to `%s' (%s)", + narg, ar.name, extramsg); +} + + +LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) { + const char *msg = lua_pushfstring(L, "%s expected, got %s", + tname, lua_typename(L, lua_type(L,narg))); + return luaL_argerror(L, narg, msg); +} + + +static void tag_error (lua_State *L, int narg, int tag) { + luaL_typerror(L, narg, lua_typename(L, tag)); +} + + +LUALIB_API void luaL_where (lua_State *L, int level) { + lua_Debug ar; + if (lua_getstack(L, level, &ar)) { /* check function at level */ + lua_getinfo(L, "Snl", &ar); /* get info about it */ + if (ar.currentline > 0) { /* is there info? */ + lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); + return; + } + } + lua_pushliteral(L, ""); /* else, no information available... */ +} + + +LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + luaL_where(L, 1); + lua_pushvfstring(L, fmt, argp); + va_end(argp); + lua_concat(L, 2); + return lua_error(L); +} + +/* }====================================================== */ + + +LUALIB_API int luaL_findstring (const char *name, const char *const list[]) { + int i; + for (i=0; list[i]; i++) + if (strcmp(list[i], name) == 0) + return i; + return -1; /* name not found */ +} + + +LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { + lua_pushstring(L, tname); + lua_rawget(L, LUA_REGISTRYINDEX); /* get registry.name */ + if (!lua_isnil(L, -1)) /* name already in use? */ + return 0; /* leave previous value on top, but return 0 */ + lua_pop(L, 1); + lua_newtable(L); /* create metatable */ + lua_pushstring(L, tname); + lua_pushvalue(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); /* registry.name = metatable */ + lua_pushvalue(L, -1); + lua_pushstring(L, tname); + lua_rawset(L, LUA_REGISTRYINDEX); /* registry[metatable] = name */ + return 1; +} + + +LUALIB_API void luaL_getmetatable (lua_State *L, const char *tname) { + lua_pushstring(L, tname); + lua_rawget(L, LUA_REGISTRYINDEX); +} + + +LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { + const char *tn; + if (!lua_getmetatable(L, ud)) return NULL; /* no metatable? */ + lua_rawget(L, LUA_REGISTRYINDEX); /* get registry[metatable] */ + tn = lua_tostring(L, -1); + if (tn && (strcmp(tn, tname) == 0)) { + lua_pop(L, 1); + return lua_touserdata(L, ud); + } + else { + lua_pop(L, 1); + return NULL; + } +} + + +LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) { + if (!lua_checkstack(L, space)) + luaL_error(L, "stack overflow (%s)", mes); +} + + +LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) { + if (lua_type(L, narg) != t) + tag_error(L, narg, t); +} + + +LUALIB_API void luaL_checkany (lua_State *L, int narg) { + if (lua_type(L, narg) == LUA_TNONE) + luaL_argerror(L, narg, "value expected"); +} + + +LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) { + const char *s = lua_tostring(L, narg); + if (!s) tag_error(L, narg, LUA_TSTRING); + if (len) *len = lua_strlen(L, narg); + return s; +} + + +LUALIB_API const char *luaL_optlstring (lua_State *L, int narg, + const char *def, size_t *len) { + if (lua_isnoneornil(L, narg)) { + if (len) + *len = (def ? strlen(def) : 0); + return def; + } + else return luaL_checklstring(L, narg, len); +} + + +LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) { + lua_Number d = lua_tonumber(L, narg); + if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ + tag_error(L, narg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) { + if (lua_isnoneornil(L, narg)) return def; + else return luaL_checknumber(L, narg); +} + + +LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { + if (!lua_getmetatable(L, obj)) /* no metatable? */ + return 0; + lua_pushstring(L, event); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); /* remove metatable and metafield */ + return 0; + } + else { + lua_remove(L, -2); /* remove only metatable */ + return 1; + } +} + + +LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { + obj = abs_index(L, obj); + if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ + return 0; + lua_pushvalue(L, obj); + lua_call(L, 1, 1); + return 1; +} + + +LUALIB_API void luaL_openlib (lua_State *L, const char *libname, + const luaL_reg *l, int nup) { + if (libname) { + lua_pushstring(L, libname); + lua_gettable(L, LUA_GLOBALSINDEX); /* check whether lib already exists */ + if (lua_isnil(L, -1)) { /* no? */ + lua_pop(L, 1); + lua_newtable(L); /* create it */ + lua_pushstring(L, libname); + lua_pushvalue(L, -2); + lua_settable(L, LUA_GLOBALSINDEX); /* register it with given name */ + } + lua_insert(L, -(nup+1)); /* move library table to below upvalues */ + } + for (; l->name; l++) { + int i; + lua_pushstring(L, l->name); + for (i=0; ifunc, nup); + lua_settable(L, -(nup+3)); + } + lua_pop(L, nup); /* remove upvalues */ +} + + + +/* +** {====================================================== +** getn-setn: size for arrays +** ======================================================= +*/ + +static int checkint (lua_State *L, int topop) { + int n = (int)lua_tonumber(L, -1); + if (n == 0 && !lua_isnumber(L, -1)) n = -1; + lua_pop(L, topop); + return n; +} + + +static void getsizes (lua_State *L) { + lua_rawgeti(L, LUA_REGISTRYINDEX, ARRAYSIZE_REF); + if (lua_isnil(L, -1)) { /* no `size' table? */ + lua_pop(L, 1); /* remove nil */ + lua_newtable(L); /* create it */ + lua_pushvalue(L, -1); /* `size' will be its own metatable */ + lua_setmetatable(L, -2); + lua_pushliteral(L, "__mode"); + lua_pushliteral(L, "k"); + lua_rawset(L, -3); /* metatable(N).__mode = "k" */ + lua_pushvalue(L, -1); + lua_rawseti(L, LUA_REGISTRYINDEX, ARRAYSIZE_REF); /* store in register */ + } +} + + +void luaL_setn (lua_State *L, int t, int n) { + t = abs_index(L, t); + lua_pushliteral(L, "n"); + lua_rawget(L, t); + if (checkint(L, 1) >= 0) { /* is there a numeric field `n'? */ + lua_pushliteral(L, "n"); /* use it */ + lua_pushnumber(L, (lua_Number)n); + lua_rawset(L, t); + } + else { /* use `sizes' */ + getsizes(L); + lua_pushvalue(L, t); + lua_pushnumber(L, (lua_Number)n); + lua_rawset(L, -3); /* sizes[t] = n */ + lua_pop(L, 1); /* remove `sizes' */ + } +} + + +int luaL_getn (lua_State *L, int t) { + int n; + t = abs_index(L, t); + lua_pushliteral(L, "n"); /* try t.n */ + lua_rawget(L, t); + if ((n = checkint(L, 1)) >= 0) return n; + getsizes(L); /* else try sizes[t] */ + lua_pushvalue(L, t); + lua_rawget(L, -2); + if ((n = checkint(L, 2)) >= 0) return n; + for (n = 1; ; n++) { /* else must count elements */ + lua_rawgeti(L, t, n); + if (lua_isnil(L, -1)) break; + lua_pop(L, 1); + } + lua_pop(L, 1); + return n - 1; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + + +#define bufflen(B) ((B)->p - (B)->buffer) +#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B))) + +#define LIMIT (LUA_MINSTACK/2) + + +static int emptybuffer (luaL_Buffer *B) { + size_t l = bufflen(B); + if (l == 0) return 0; /* put nothing on stack */ + else { + lua_pushlstring(B->L, B->buffer, l); + B->p = B->buffer; + B->lvl++; + return 1; + } +} + + +static void adjuststack (luaL_Buffer *B) { + if (B->lvl > 1) { + lua_State *L = B->L; + int toget = 1; /* number of levels to concat */ + size_t toplen = lua_strlen(L, -1); + do { + size_t l = lua_strlen(L, -(toget+1)); + if (B->lvl - toget + 1 >= LIMIT || toplen > l) { + toplen += l; + toget++; + } + else break; + } while (toget < B->lvl); + lua_concat(L, toget); + B->lvl = B->lvl - toget + 1; + } +} + + +LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) { + if (emptybuffer(B)) + adjuststack(B); + return B->buffer; +} + + +LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { + while (l--) + luaL_putchar(B, *s++); +} + + +LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { + luaL_addlstring(B, s, strlen(s)); +} + + +LUALIB_API void luaL_pushresult (luaL_Buffer *B) { + emptybuffer(B); + lua_concat(B->L, B->lvl); + B->lvl = 1; +} + + +LUALIB_API void luaL_addvalue (luaL_Buffer *B) { + lua_State *L = B->L; + size_t vl = lua_strlen(L, -1); + if (vl <= bufffree(B)) { /* fit into buffer? */ + memcpy(B->p, lua_tostring(L, -1), vl); /* put it there */ + B->p += vl; + lua_pop(L, 1); /* remove from stack */ + } + else { + if (emptybuffer(B)) + lua_insert(L, -2); /* put buffer before new value */ + B->lvl++; /* add new value into B stack */ + adjuststack(B); + } +} + + +LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { + B->L = L; + B->p = B->buffer; + B->lvl = 0; +} + +/* }====================================================== */ + + +LUALIB_API int luaL_ref (lua_State *L, int t) { + int ref; + t = abs_index(L, t); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* remove from stack */ + return LUA_REFNIL; /* `nil' has a unique fixed reference */ + } + lua_rawgeti(L, t, FREELIST_REF); /* get first free element */ + ref = (int)lua_tonumber(L, -1); /* ref = t[FREELIST_REF] */ + lua_pop(L, 1); /* remove it from stack */ + if (ref != 0) { /* any free element? */ + lua_rawgeti(L, t, ref); /* remove it from list */ + lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */ + } + else { /* no free elements */ + ref = luaL_getn(L, t); + if (ref < RESERVED_REFS) + ref = RESERVED_REFS; /* skip reserved references */ + ref++; /* create new reference */ + luaL_setn(L, t, ref); + } + lua_rawseti(L, t, ref); + return ref; +} + + +LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { + if (ref >= 0) { + t = abs_index(L, t); + lua_rawgeti(L, t, FREELIST_REF); + lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */ + lua_pushnumber(L, (lua_Number)ref); + lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */ + } +} + + + +/* +** {====================================================== +** Load functions +** ======================================================= +*/ + +typedef struct LoadF { + FILE *f; + char buff[LUAL_BUFFERSIZE]; +} LoadF; + + +static const char *getF (lua_State *L, void *ud, size_t *size) { + LoadF *lf = (LoadF *)ud; + (void)L; + if (feof(lf->f)) return NULL; + *size = fread(lf->buff, 1, LUAL_BUFFERSIZE, lf->f); + return (*size > 0) ? lf->buff : NULL; +} + + +static int errfile (lua_State *L, int fnameindex) { + const char *filename = lua_tostring(L, fnameindex) + 1; + lua_pushfstring(L, "cannot read %s: %s", filename, strerror(errno)); + lua_remove(L, fnameindex); + return LUA_ERRFILE; +} + + +LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) { + LoadF lf; + int status, readstatus; + int c; + int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ + if (filename == NULL) { + lua_pushliteral(L, "=stdin"); + lf.f = stdin; + } + else { + lua_pushfstring(L, "@%s", filename); + lf.f = fopen(filename, "r"); + } + if (lf.f == NULL) return errfile(L, fnameindex); /* unable to open file */ + c = ungetc(getc(lf.f), lf.f); + if (!(isspace(c) || isprint(c)) && lf.f != stdin) { /* binary file? */ + fclose(lf.f); + lf.f = fopen(filename, "rb"); /* reopen in binary mode */ + if (lf.f == NULL) return errfile(L, fnameindex); /* unable to reopen file */ + } + status = lua_load(L, getF, &lf, lua_tostring(L, -1)); + readstatus = ferror(lf.f); + if (lf.f != stdin) fclose(lf.f); /* close file (even in case of errors) */ + if (readstatus) { + lua_settop(L, fnameindex); /* ignore results from `lua_load' */ + return errfile(L, fnameindex); + } + lua_remove(L, fnameindex); + return status; +} + + +typedef struct LoadS { + const char *s; + size_t size; +} LoadS; + + +static const char *getS (lua_State *L, void *ud, size_t *size) { + LoadS *ls = (LoadS *)ud; + (void)L; + if (ls->size == 0) return NULL; + *size = ls->size; + ls->size = 0; + return ls->s; +} + + +LUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size, + const char *name) { + LoadS ls; + ls.s = buff; + ls.size = size; + return lua_load(L, getS, &ls, name); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** compatibility code +** ======================================================= +*/ + + +static void callalert (lua_State *L, int status) { + if (status != 0) { + lua_getglobal(L, "_ALERT"); + if (lua_isfunction(L, -1)) { + lua_insert(L, -2); + lua_call(L, 1, 0); + } + else { /* no _ALERT function; print it on stderr */ + fprintf(stderr, "%s\n", lua_tostring(L, -2)); + lua_pop(L, 2); /* remove error message and _ALERT */ + } + } +} + + +static int aux_do (lua_State *L, int status) { + if (status == 0) { /* parse OK? */ + status = lua_pcall(L, 0, LUA_MULTRET, 0); /* call main */ + } + callalert(L, status); + return status; +} + + +LUALIB_API int lua_dofile (lua_State *L, const char *filename) { + return aux_do(L, luaL_loadfile(L, filename)); +} + + +LUALIB_API int lua_dobuffer (lua_State *L, const char *buff, size_t size, + const char *name) { + return aux_do(L, luaL_loadbuffer(L, buff, size, name)); +} + + +LUALIB_API int lua_dostring (lua_State *L, const char *str) { + return lua_dobuffer(L, str, strlen(str), str); +} + +/* }====================================================== */ diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lbaselib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lbaselib.c index 1b320924d2..9f613bcdb9 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lbaselib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lbaselib.c @@ -1,684 +1,684 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - - -#include -#include -#include -#include - -#define lbaselib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - - - -/* -** If your system does not support `stdout', you can just remove this function. -** If you need, you can define your own `print' function, following this -** model but changing `fputs' to put the strings at a proper place -** (a console window or a log file, for instance). -*/ -static int luaB_print (lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - int i; - lua_getglobal(L, "tostring"); - for (i=1; i<=n; i++) { - const char *s; - lua_pushvalue(L, -1); /* function to be called */ - lua_pushvalue(L, i); /* value to print */ - lua_call(L, 1, 1); - s = lua_tostring(L, -1); /* get result */ - if (s == NULL) - return luaL_error(L, "`tostring' must return a string to `print'"); - if (i>1) fputs("\t", stdout); - fputs(s, stdout); - lua_pop(L, 1); /* pop result */ - } - fputs("\n", stdout); - return 0; -} - - -static int luaB_tonumber (lua_State *L) { - int base = luaL_optint(L, 2, 10); - if (base == 10) { /* standard conversion */ - luaL_checkany(L, 1); - if (lua_isnumber(L, 1)) { - lua_pushnumber(L, lua_tonumber(L, 1)); - return 1; - } - } - else { - const char *s1 = luaL_checkstring(L, 1); - char *s2; - unsigned long n; - luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); - n = strtoul(s1, &s2, base); - if (s1 != s2) { /* at least one valid digit? */ - while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */ - if (*s2 == '\0') { /* no invalid trailing characters? */ - lua_pushnumber(L, (lua_Number)n); - return 1; - } - } - } - lua_pushnil(L); /* else not a number */ - return 1; -} - - -static int luaB_error (lua_State *L) { - int level = luaL_optint(L, 2, 1); - luaL_checkany(L, 1); - if (!lua_isstring(L, 1) || level == 0) - lua_pushvalue(L, 1); /* propagate error message without changes */ - else { /* add extra information */ - luaL_where(L, level); - lua_pushvalue(L, 1); - lua_concat(L, 2); - } - return lua_error(L); -} - - -static int luaB_getmetatable (lua_State *L) { - luaL_checkany(L, 1); - if (!lua_getmetatable(L, 1)) { - lua_pushnil(L); - return 1; /* no metatable */ - } - luaL_getmetafield(L, 1, "__metatable"); - return 1; /* returns either __metatable field (if present) or metatable */ -} - - -static int luaB_setmetatable (lua_State *L) { - int t = lua_type(L, 2); - luaL_checktype(L, 1, LUA_TTABLE); - luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, - "nil or table expected"); - if (luaL_getmetafield(L, 1, "__metatable")) - luaL_error(L, "cannot change a protected metatable"); - lua_settop(L, 2); - lua_setmetatable(L, 1); - return 1; -} - - -static void getfunc (lua_State *L) { - if (lua_isfunction(L, 1)) lua_pushvalue(L, 1); - else { - lua_Debug ar; - int level = luaL_optint(L, 1, 1); - luaL_argcheck(L, level >= 0, 1, "level must be non-negative"); - if (lua_getstack(L, level, &ar) == 0) - luaL_argerror(L, 1, "invalid level"); - lua_getinfo(L, "f", &ar); - if (lua_isnil(L, -1)) - luaL_error(L, "no function environment for tail call at level %d", - level); - } -} - - -static int aux_getfenv (lua_State *L) { - lua_getfenv(L, -1); - lua_pushliteral(L, "__fenv"); - lua_rawget(L, -2); - return !lua_isnil(L, -1); -} - - -static int luaB_getfenv (lua_State *L) { - getfunc(L); - if (!aux_getfenv(L)) /* __fenv not defined? */ - lua_pop(L, 1); /* remove it, to return real environment */ - return 1; -} - - -static int luaB_setfenv (lua_State *L) { - luaL_checktype(L, 2, LUA_TTABLE); - getfunc(L); - if (aux_getfenv(L)) /* __fenv defined? */ - luaL_error(L, "`setfenv' cannot change a protected environment"); - else - lua_pop(L, 2); /* remove __fenv and real environment table */ - lua_pushvalue(L, 2); - if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) - lua_replace(L, LUA_GLOBALSINDEX); - else if (lua_setfenv(L, -2) == 0) - luaL_error(L, "`setfenv' cannot change environment of given function"); - return 0; -} - - -static int luaB_rawequal (lua_State *L) { - luaL_checkany(L, 1); - luaL_checkany(L, 2); - lua_pushboolean(L, lua_rawequal(L, 1, 2)); - return 1; -} - - -static int luaB_rawget (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - luaL_checkany(L, 2); - lua_rawget(L, 1); - return 1; -} - -static int luaB_rawset (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - luaL_checkany(L, 2); - luaL_checkany(L, 3); - lua_rawset(L, 1); - return 1; -} - - -static int luaB_gcinfo (lua_State *L) { - lua_pushnumber(L, (lua_Number)lua_getgccount(L)); - lua_pushnumber(L, (lua_Number)lua_getgcthreshold(L)); - return 2; -} - - -static int luaB_collectgarbage (lua_State *L) { - lua_setgcthreshold(L, luaL_optint(L, 1, 0)); - return 0; -} - - -static int luaB_type (lua_State *L) { - luaL_checkany(L, 1); - lua_pushstring(L, lua_typename(L, lua_type(L, 1))); - return 1; -} - - -static int luaB_next (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - lua_settop(L, 2); /* create a 2nd argument if there isn't one */ - if (lua_next(L, 1)) - return 2; - else { - lua_pushnil(L); - return 1; - } -} - - -static int luaB_pairs (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - lua_pushliteral(L, "next"); - lua_rawget(L, LUA_GLOBALSINDEX); /* return generator, */ - lua_pushvalue(L, 1); /* state, */ - lua_pushnil(L); /* and initial value */ - return 3; -} - - -static int luaB_ipairs (lua_State *L) { - lua_Number i = lua_tonumber(L, 2); - luaL_checktype(L, 1, LUA_TTABLE); - if (i == 0 && lua_isnone(L, 2)) { /* `for' start? */ - lua_pushliteral(L, "ipairs"); - lua_rawget(L, LUA_GLOBALSINDEX); /* return generator, */ - lua_pushvalue(L, 1); /* state, */ - lua_pushnumber(L, 0); /* and initial value */ - return 3; - } - else { /* `for' step */ - i++; /* next value */ - lua_pushnumber(L, i); - lua_rawgeti(L, 1, (int)i); - return (lua_isnil(L, -1)) ? 0 : 2; - } -} - - -static int load_aux (lua_State *L, int status) { - if (status == 0) /* OK? */ - return 1; - else { - lua_pushnil(L); - lua_insert(L, -2); /* put before error message */ - return 2; /* return nil plus error message */ - } -} - - -static int luaB_loadstring (lua_State *L) { - size_t l; - const char *s = luaL_checklstring(L, 1, &l); - const char *chunkname = luaL_optstring(L, 2, s); - return load_aux(L, luaL_loadbuffer(L, s, l, chunkname)); -} - - -static int luaB_loadfile (lua_State *L) { - const char *fname = luaL_optstring(L, 1, NULL); - return load_aux(L, luaL_loadfile(L, fname)); -} - - -static int luaB_dofile (lua_State *L) { - const char *fname = luaL_optstring(L, 1, NULL); - int status = luaL_loadfile(L, fname); - if (status != 0) lua_error(L); - lua_call(L, 0, LUA_MULTRET); - return lua_gettop(L) - 1; -} - - -static int luaB_assert (lua_State *L) { - luaL_checkany(L, 1); - if (!lua_toboolean(L, 1)) - return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!")); - lua_settop(L, 1); - return 1; -} - - -static int luaB_unpack (lua_State *L) { - int n, i; - luaL_checktype(L, 1, LUA_TTABLE); - n = luaL_getn(L, 1); - luaL_checkstack(L, n, "table too big to unpack"); - for (i=1; i<=n; i++) /* push arg[1...n] */ - lua_rawgeti(L, 1, i); - return n; -} - - -static int luaB_pcall (lua_State *L) { - int status; - luaL_checkany(L, 1); - status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); - lua_pushboolean(L, (status == 0)); - lua_insert(L, 1); - return lua_gettop(L); /* return status + all results */ -} - - -static int luaB_xpcall (lua_State *L) { - int status; - luaL_checkany(L, 2); - lua_settop(L, 2); - lua_insert(L, 1); /* put error function under function to be called */ - status = lua_pcall(L, 0, LUA_MULTRET, 1); - lua_pushboolean(L, (status == 0)); - lua_replace(L, 1); - return lua_gettop(L); /* return status + all results */ -} - - -static int luaB_tostring (lua_State *L) { - char buff[64]; - luaL_checkany(L, 1); - if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */ - return 1; /* use its value */ - switch (lua_type(L, 1)) { - case LUA_TNUMBER: - lua_pushstring(L, lua_tostring(L, 1)); - return 1; - case LUA_TSTRING: - lua_pushvalue(L, 1); - return 1; - case LUA_TBOOLEAN: - lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false")); - return 1; - case LUA_TTABLE: - sprintf(buff, "table: %p", lua_topointer(L, 1)); - break; - case LUA_TFUNCTION: - sprintf(buff, "function: %p", lua_topointer(L, 1)); - break; - case LUA_TUSERDATA: - case LUA_TLIGHTUSERDATA: - sprintf(buff, "userdata: %p", lua_touserdata(L, 1)); - break; - case LUA_TTHREAD: - sprintf(buff, "thread: %p", (void *)lua_tothread(L, 1)); - break; - case LUA_TNIL: - lua_pushliteral(L, "nil"); - return 1; - } - lua_pushstring(L, buff); - return 1; -} - - -static int luaB_newproxy (lua_State *L) { - lua_settop(L, 1); - lua_newuserdata(L, 0); /* create proxy */ - if (lua_toboolean(L, 1) == 0) - return 1; /* no metatable */ - else if (lua_isboolean(L, 1)) { - lua_newtable(L); /* create a new metatable `m' ... */ - lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */ - lua_pushboolean(L, 1); - lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */ - } - else { - int validproxy = 0; /* to check if weaktable[metatable(u)] == true */ - if (lua_getmetatable(L, 1)) { - lua_rawget(L, lua_upvalueindex(1)); - validproxy = lua_toboolean(L, -1); - lua_pop(L, 1); /* remove value */ - } - luaL_argcheck(L, validproxy, 1, "boolean or proxy expected"); - lua_getmetatable(L, 1); /* metatable is valid; get it */ - } - lua_setmetatable(L, 2); - return 1; -} - - -/* -** {====================================================== -** `require' function -** ======================================================= -*/ - - -/* name of global that holds table with loaded packages */ -#define REQTAB "_LOADED" - -/* name of global that holds the search path for packages */ -#define LUA_PATH "LUA_PATH" - -#ifndef LUA_PATH_SEP -#define LUA_PATH_SEP ';' -#endif - -#ifndef LUA_PATH_MARK -#define LUA_PATH_MARK '?' -#endif - -#ifndef LUA_PATH_DEFAULT -#define LUA_PATH_DEFAULT "?;?.lua" -#endif - - -static const char *getpath (lua_State *L) { - const char *path; - lua_getglobal(L, LUA_PATH); /* try global variable */ - path = lua_tostring(L, -1); - lua_pop(L, 1); - if (path) return path; - path = getenv(LUA_PATH); /* else try environment variable */ - if (path) return path; - return LUA_PATH_DEFAULT; /* else use default */ -} - - -static const char *pushnextpath (lua_State *L, const char *path) { - const char *l; - if (*path == '\0') return NULL; /* no more paths */ - if (*path == LUA_PATH_SEP) path++; /* skip separator */ - l = strchr(path, LUA_PATH_SEP); /* find next separator */ - if (l == NULL) l = path+strlen(path); - lua_pushlstring(L, path, l - path); /* directory name */ - return l; -} - - -static void pushcomposename (lua_State *L) { - const char *path = lua_tostring(L, -1); - const char *wild; - int n = 1; - while ((wild = strchr(path, LUA_PATH_MARK)) != NULL) { - /* is there stack space for prefix, name, and eventual last sufix? */ - luaL_checkstack(L, 3, "too many marks in a path component"); - lua_pushlstring(L, path, wild - path); /* push prefix */ - lua_pushvalue(L, 1); /* push package name (in place of MARK) */ - path = wild + 1; /* continue after MARK */ - n += 2; - } - lua_pushstring(L, path); /* push last sufix (`n' already includes this) */ - lua_concat(L, n); -} - - -static int luaB_require (lua_State *L) { - const char *path; - int status = LUA_ERRFILE; /* not found (yet) */ - luaL_checkstring(L, 1); - lua_settop(L, 1); - lua_getglobal(L, REQTAB); - if (!lua_istable(L, 2)) return luaL_error(L, "`" REQTAB "' is not a table"); - path = getpath(L); - lua_pushvalue(L, 1); /* check package's name in book-keeping table */ - lua_rawget(L, 2); - if (lua_toboolean(L, -1)) /* is it there? */ - return 1; /* package is already loaded; return its result */ - else { /* must load it */ - while (status == LUA_ERRFILE) { - lua_settop(L, 3); /* reset stack position */ - if ((path = pushnextpath(L, path)) == NULL) break; - pushcomposename(L); - status = luaL_loadfile(L, lua_tostring(L, -1)); /* try to load it */ - } - } - switch (status) { - case 0: { - lua_getglobal(L, "_REQUIREDNAME"); /* save previous name */ - lua_insert(L, -2); /* put it below function */ - lua_pushvalue(L, 1); - lua_setglobal(L, "_REQUIREDNAME"); /* set new name */ - lua_call(L, 0, 1); /* run loaded module */ - lua_insert(L, -2); /* put result below previous name */ - lua_setglobal(L, "_REQUIREDNAME"); /* reset to previous name */ - if (lua_isnil(L, -1)) { /* no/nil return? */ - lua_pushboolean(L, 1); - lua_replace(L, -2); /* replace to true */ - } - lua_pushvalue(L, 1); - lua_pushvalue(L, -2); - lua_rawset(L, 2); /* mark it as loaded */ - return 1; /* return value */ - } - case LUA_ERRFILE: { /* file not found */ - return luaL_error(L, "could not load package `%s' from path `%s'", - lua_tostring(L, 1), getpath(L)); - } - default: { - return luaL_error(L, "error loading package `%s' (%s)", - lua_tostring(L, 1), lua_tostring(L, -1)); - } - } -} - -/* }====================================================== */ - - -static const luaL_reg base_funcs[] = { - {"error", luaB_error}, - {"getmetatable", luaB_getmetatable}, - {"setmetatable", luaB_setmetatable}, - {"getfenv", luaB_getfenv}, - {"setfenv", luaB_setfenv}, - {"next", luaB_next}, - {"ipairs", luaB_ipairs}, - {"pairs", luaB_pairs}, - {"print", luaB_print}, - {"tonumber", luaB_tonumber}, - {"tostring", luaB_tostring}, - {"type", luaB_type}, - {"assert", luaB_assert}, - {"unpack", luaB_unpack}, - {"rawequal", luaB_rawequal}, - {"rawget", luaB_rawget}, - {"rawset", luaB_rawset}, - {"pcall", luaB_pcall}, - {"xpcall", luaB_xpcall}, - {"collectgarbage", luaB_collectgarbage}, - {"gcinfo", luaB_gcinfo}, - {"loadfile", luaB_loadfile}, - {"dofile", luaB_dofile}, - {"loadstring", luaB_loadstring}, - {"require", luaB_require}, - {NULL, NULL} -}; - - -/* -** {====================================================== -** Coroutine library -** ======================================================= -*/ - -static int auxresume (lua_State *L, lua_State *co, int narg) { - int status; - if (!lua_checkstack(co, narg)) - luaL_error(L, "too many arguments to resume"); - lua_xmove(L, co, narg); - status = lua_resume(co, narg); - if (status == 0) { - int nres = lua_gettop(co); - if (!lua_checkstack(L, nres)) - luaL_error(L, "too many results to resume"); - lua_xmove(co, L, nres); /* move yielded values */ - return nres; - } - else { - lua_xmove(co, L, 1); /* move error message */ - return -1; /* error flag */ - } -} - - -static int luaB_coresume (lua_State *L) { - lua_State *co = lua_tothread(L, 1); - int r; - luaL_argcheck(L, co, 1, "coroutine expected"); - r = auxresume(L, co, lua_gettop(L) - 1); - if (r < 0) { - lua_pushboolean(L, 0); - lua_insert(L, -2); - return 2; /* return false + error message */ - } - else { - lua_pushboolean(L, 1); - lua_insert(L, -(r + 1)); - return r + 1; /* return true + `resume' returns */ - } -} - - -static int luaB_auxwrap (lua_State *L) { - lua_State *co = lua_tothread(L, lua_upvalueindex(1)); - int r = auxresume(L, co, lua_gettop(L)); - if (r < 0) { - if (lua_isstring(L, -1)) { /* error object is a string? */ - luaL_where(L, 1); /* add extra info */ - lua_insert(L, -2); - lua_concat(L, 2); - } - lua_error(L); /* propagate error */ - } - return r; -} - - -static int luaB_cocreate (lua_State *L) { - lua_State *NL = lua_newthread(L); - luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, - "Lua function expected"); - lua_pushvalue(L, 1); /* move function to top */ - lua_xmove(L, NL, 1); /* move function from L to NL */ - return 1; -} - - -static int luaB_cowrap (lua_State *L) { - luaB_cocreate(L); - lua_pushcclosure(L, luaB_auxwrap, 1); - return 1; -} - - -static int luaB_yield (lua_State *L) { - return lua_yield(L, lua_gettop(L)); -} - - -static int luaB_costatus (lua_State *L) { - lua_State *co = lua_tothread(L, 1); - luaL_argcheck(L, co, 1, "coroutine expected"); - if (L == co) lua_pushliteral(L, "running"); - else { - lua_Debug ar; - if (lua_getstack(co, 0, &ar) == 0 && lua_gettop(co) == 0) - lua_pushliteral(L, "dead"); - else - lua_pushliteral(L, "suspended"); - } - return 1; -} - - -static const luaL_reg co_funcs[] = { - {"create", luaB_cocreate}, - {"wrap", luaB_cowrap}, - {"resume", luaB_coresume}, - {"yield", luaB_yield}, - {"status", luaB_costatus}, - {NULL, NULL} -}; - -/* }====================================================== */ - - - -static void base_open (lua_State *L) { - lua_pushliteral(L, "_G"); - lua_pushvalue(L, LUA_GLOBALSINDEX); - luaL_openlib(L, NULL, base_funcs, 0); /* open lib into global table */ - lua_pushliteral(L, "_VERSION"); - lua_pushliteral(L, LUA_VERSION); - lua_rawset(L, -3); /* set global _VERSION */ - /* `newproxy' needs a weaktable as upvalue */ - lua_pushliteral(L, "newproxy"); - lua_newtable(L); /* new table `w' */ - lua_pushvalue(L, -1); /* `w' will be its own metatable */ - lua_setmetatable(L, -2); - lua_pushliteral(L, "__mode"); - lua_pushliteral(L, "k"); - lua_rawset(L, -3); /* metatable(w).__mode = "k" */ - lua_pushcclosure(L, luaB_newproxy, 1); - lua_rawset(L, -3); /* set global `newproxy' */ - lua_rawset(L, -1); /* set global _G */ -} - - -LUALIB_API int luaopen_base (lua_State *L) { - base_open(L); - luaL_openlib(L, LUA_COLIBNAME, co_funcs, 0); - lua_newtable(L); - lua_setglobal(L, REQTAB); - return 0; -} - +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + + +#include +#include +#include +#include + +#define lbaselib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + + +/* +** If your system does not support `stdout', you can just remove this function. +** If you need, you can define your own `print' function, following this +** model but changing `fputs' to put the strings at a proper place +** (a console window or a log file, for instance). +*/ +static int luaB_print (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + lua_getglobal(L, "tostring"); + for (i=1; i<=n; i++) { + const char *s; + lua_pushvalue(L, -1); /* function to be called */ + lua_pushvalue(L, i); /* value to print */ + lua_call(L, 1, 1); + s = lua_tostring(L, -1); /* get result */ + if (s == NULL) + return luaL_error(L, "`tostring' must return a string to `print'"); + if (i>1) fputs("\t", stdout); + fputs(s, stdout); + lua_pop(L, 1); /* pop result */ + } + fputs("\n", stdout); + return 0; +} + + +static int luaB_tonumber (lua_State *L) { + int base = luaL_optint(L, 2, 10); + if (base == 10) { /* standard conversion */ + luaL_checkany(L, 1); + if (lua_isnumber(L, 1)) { + lua_pushnumber(L, lua_tonumber(L, 1)); + return 1; + } + } + else { + const char *s1 = luaL_checkstring(L, 1); + char *s2; + unsigned long n; + luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); + n = strtoul(s1, &s2, base); + if (s1 != s2) { /* at least one valid digit? */ + while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */ + if (*s2 == '\0') { /* no invalid trailing characters? */ + lua_pushnumber(L, (lua_Number)n); + return 1; + } + } + } + lua_pushnil(L); /* else not a number */ + return 1; +} + + +static int luaB_error (lua_State *L) { + int level = luaL_optint(L, 2, 1); + luaL_checkany(L, 1); + if (!lua_isstring(L, 1) || level == 0) + lua_pushvalue(L, 1); /* propagate error message without changes */ + else { /* add extra information */ + luaL_where(L, level); + lua_pushvalue(L, 1); + lua_concat(L, 2); + } + return lua_error(L); +} + + +static int luaB_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); + return 1; /* no metatable */ + } + luaL_getmetafield(L, 1, "__metatable"); + return 1; /* returns either __metatable field (if present) or metatable */ +} + + +static int luaB_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, + "nil or table expected"); + if (luaL_getmetafield(L, 1, "__metatable")) + luaL_error(L, "cannot change a protected metatable"); + lua_settop(L, 2); + lua_setmetatable(L, 1); + return 1; +} + + +static void getfunc (lua_State *L) { + if (lua_isfunction(L, 1)) lua_pushvalue(L, 1); + else { + lua_Debug ar; + int level = luaL_optint(L, 1, 1); + luaL_argcheck(L, level >= 0, 1, "level must be non-negative"); + if (lua_getstack(L, level, &ar) == 0) + luaL_argerror(L, 1, "invalid level"); + lua_getinfo(L, "f", &ar); + if (lua_isnil(L, -1)) + luaL_error(L, "no function environment for tail call at level %d", + level); + } +} + + +static int aux_getfenv (lua_State *L) { + lua_getfenv(L, -1); + lua_pushliteral(L, "__fenv"); + lua_rawget(L, -2); + return !lua_isnil(L, -1); +} + + +static int luaB_getfenv (lua_State *L) { + getfunc(L); + if (!aux_getfenv(L)) /* __fenv not defined? */ + lua_pop(L, 1); /* remove it, to return real environment */ + return 1; +} + + +static int luaB_setfenv (lua_State *L) { + luaL_checktype(L, 2, LUA_TTABLE); + getfunc(L); + if (aux_getfenv(L)) /* __fenv defined? */ + luaL_error(L, "`setfenv' cannot change a protected environment"); + else + lua_pop(L, 2); /* remove __fenv and real environment table */ + lua_pushvalue(L, 2); + if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) + lua_replace(L, LUA_GLOBALSINDEX); + else if (lua_setfenv(L, -2) == 0) + luaL_error(L, "`setfenv' cannot change environment of given function"); + return 0; +} + + +static int luaB_rawequal (lua_State *L) { + luaL_checkany(L, 1); + luaL_checkany(L, 2); + lua_pushboolean(L, lua_rawequal(L, 1, 2)); + return 1; +} + + +static int luaB_rawget (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + lua_rawget(L, 1); + return 1; +} + +static int luaB_rawset (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + luaL_checkany(L, 3); + lua_rawset(L, 1); + return 1; +} + + +static int luaB_gcinfo (lua_State *L) { + lua_pushnumber(L, (lua_Number)lua_getgccount(L)); + lua_pushnumber(L, (lua_Number)lua_getgcthreshold(L)); + return 2; +} + + +static int luaB_collectgarbage (lua_State *L) { + lua_setgcthreshold(L, luaL_optint(L, 1, 0)); + return 0; +} + + +static int luaB_type (lua_State *L) { + luaL_checkany(L, 1); + lua_pushstring(L, lua_typename(L, lua_type(L, 1))); + return 1; +} + + +static int luaB_next (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 2); /* create a 2nd argument if there isn't one */ + if (lua_next(L, 1)) + return 2; + else { + lua_pushnil(L); + return 1; + } +} + + +static int luaB_pairs (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushliteral(L, "next"); + lua_rawget(L, LUA_GLOBALSINDEX); /* return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushnil(L); /* and initial value */ + return 3; +} + + +static int luaB_ipairs (lua_State *L) { + lua_Number i = lua_tonumber(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + if (i == 0 && lua_isnone(L, 2)) { /* `for' start? */ + lua_pushliteral(L, "ipairs"); + lua_rawget(L, LUA_GLOBALSINDEX); /* return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushnumber(L, 0); /* and initial value */ + return 3; + } + else { /* `for' step */ + i++; /* next value */ + lua_pushnumber(L, i); + lua_rawgeti(L, 1, (int)i); + return (lua_isnil(L, -1)) ? 0 : 2; + } +} + + +static int load_aux (lua_State *L, int status) { + if (status == 0) /* OK? */ + return 1; + else { + lua_pushnil(L); + lua_insert(L, -2); /* put before error message */ + return 2; /* return nil plus error message */ + } +} + + +static int luaB_loadstring (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + const char *chunkname = luaL_optstring(L, 2, s); + return load_aux(L, luaL_loadbuffer(L, s, l, chunkname)); +} + + +static int luaB_loadfile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + return load_aux(L, luaL_loadfile(L, fname)); +} + + +static int luaB_dofile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + int status = luaL_loadfile(L, fname); + if (status != 0) lua_error(L); + lua_call(L, 0, LUA_MULTRET); + return lua_gettop(L) - 1; +} + + +static int luaB_assert (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_toboolean(L, 1)) + return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!")); + lua_settop(L, 1); + return 1; +} + + +static int luaB_unpack (lua_State *L) { + int n, i; + luaL_checktype(L, 1, LUA_TTABLE); + n = luaL_getn(L, 1); + luaL_checkstack(L, n, "table too big to unpack"); + for (i=1; i<=n; i++) /* push arg[1...n] */ + lua_rawgeti(L, 1, i); + return n; +} + + +static int luaB_pcall (lua_State *L) { + int status; + luaL_checkany(L, 1); + status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); + lua_pushboolean(L, (status == 0)); + lua_insert(L, 1); + return lua_gettop(L); /* return status + all results */ +} + + +static int luaB_xpcall (lua_State *L) { + int status; + luaL_checkany(L, 2); + lua_settop(L, 2); + lua_insert(L, 1); /* put error function under function to be called */ + status = lua_pcall(L, 0, LUA_MULTRET, 1); + lua_pushboolean(L, (status == 0)); + lua_replace(L, 1); + return lua_gettop(L); /* return status + all results */ +} + + +static int luaB_tostring (lua_State *L) { + char buff[64]; + luaL_checkany(L, 1); + if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */ + return 1; /* use its value */ + switch (lua_type(L, 1)) { + case LUA_TNUMBER: + lua_pushstring(L, lua_tostring(L, 1)); + return 1; + case LUA_TSTRING: + lua_pushvalue(L, 1); + return 1; + case LUA_TBOOLEAN: + lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false")); + return 1; + case LUA_TTABLE: + sprintf(buff, "table: %p", lua_topointer(L, 1)); + break; + case LUA_TFUNCTION: + sprintf(buff, "function: %p", lua_topointer(L, 1)); + break; + case LUA_TUSERDATA: + case LUA_TLIGHTUSERDATA: + sprintf(buff, "userdata: %p", lua_touserdata(L, 1)); + break; + case LUA_TTHREAD: + sprintf(buff, "thread: %p", (void *)lua_tothread(L, 1)); + break; + case LUA_TNIL: + lua_pushliteral(L, "nil"); + return 1; + } + lua_pushstring(L, buff); + return 1; +} + + +static int luaB_newproxy (lua_State *L) { + lua_settop(L, 1); + lua_newuserdata(L, 0); /* create proxy */ + if (lua_toboolean(L, 1) == 0) + return 1; /* no metatable */ + else if (lua_isboolean(L, 1)) { + lua_newtable(L); /* create a new metatable `m' ... */ + lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */ + lua_pushboolean(L, 1); + lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */ + } + else { + int validproxy = 0; /* to check if weaktable[metatable(u)] == true */ + if (lua_getmetatable(L, 1)) { + lua_rawget(L, lua_upvalueindex(1)); + validproxy = lua_toboolean(L, -1); + lua_pop(L, 1); /* remove value */ + } + luaL_argcheck(L, validproxy, 1, "boolean or proxy expected"); + lua_getmetatable(L, 1); /* metatable is valid; get it */ + } + lua_setmetatable(L, 2); + return 1; +} + + +/* +** {====================================================== +** `require' function +** ======================================================= +*/ + + +/* name of global that holds table with loaded packages */ +#define REQTAB "_LOADED" + +/* name of global that holds the search path for packages */ +#define LUA_PATH "LUA_PATH" + +#ifndef LUA_PATH_SEP +#define LUA_PATH_SEP ';' +#endif + +#ifndef LUA_PATH_MARK +#define LUA_PATH_MARK '?' +#endif + +#ifndef LUA_PATH_DEFAULT +#define LUA_PATH_DEFAULT "?;?.lua" +#endif + + +static const char *getpath (lua_State *L) { + const char *path; + lua_getglobal(L, LUA_PATH); /* try global variable */ + path = lua_tostring(L, -1); + lua_pop(L, 1); + if (path) return path; + path = getenv(LUA_PATH); /* else try environment variable */ + if (path) return path; + return LUA_PATH_DEFAULT; /* else use default */ +} + + +static const char *pushnextpath (lua_State *L, const char *path) { + const char *l; + if (*path == '\0') return NULL; /* no more paths */ + if (*path == LUA_PATH_SEP) path++; /* skip separator */ + l = strchr(path, LUA_PATH_SEP); /* find next separator */ + if (l == NULL) l = path+strlen(path); + lua_pushlstring(L, path, l - path); /* directory name */ + return l; +} + + +static void pushcomposename (lua_State *L) { + const char *path = lua_tostring(L, -1); + const char *wild; + int n = 1; + while ((wild = strchr(path, LUA_PATH_MARK)) != NULL) { + /* is there stack space for prefix, name, and eventual last sufix? */ + luaL_checkstack(L, 3, "too many marks in a path component"); + lua_pushlstring(L, path, wild - path); /* push prefix */ + lua_pushvalue(L, 1); /* push package name (in place of MARK) */ + path = wild + 1; /* continue after MARK */ + n += 2; + } + lua_pushstring(L, path); /* push last sufix (`n' already includes this) */ + lua_concat(L, n); +} + + +static int luaB_require (lua_State *L) { + const char *path; + int status = LUA_ERRFILE; /* not found (yet) */ + luaL_checkstring(L, 1); + lua_settop(L, 1); + lua_getglobal(L, REQTAB); + if (!lua_istable(L, 2)) return luaL_error(L, "`" REQTAB "' is not a table"); + path = getpath(L); + lua_pushvalue(L, 1); /* check package's name in book-keeping table */ + lua_rawget(L, 2); + if (lua_toboolean(L, -1)) /* is it there? */ + return 1; /* package is already loaded; return its result */ + else { /* must load it */ + while (status == LUA_ERRFILE) { + lua_settop(L, 3); /* reset stack position */ + if ((path = pushnextpath(L, path)) == NULL) break; + pushcomposename(L); + status = luaL_loadfile(L, lua_tostring(L, -1)); /* try to load it */ + } + } + switch (status) { + case 0: { + lua_getglobal(L, "_REQUIREDNAME"); /* save previous name */ + lua_insert(L, -2); /* put it below function */ + lua_pushvalue(L, 1); + lua_setglobal(L, "_REQUIREDNAME"); /* set new name */ + lua_call(L, 0, 1); /* run loaded module */ + lua_insert(L, -2); /* put result below previous name */ + lua_setglobal(L, "_REQUIREDNAME"); /* reset to previous name */ + if (lua_isnil(L, -1)) { /* no/nil return? */ + lua_pushboolean(L, 1); + lua_replace(L, -2); /* replace to true */ + } + lua_pushvalue(L, 1); + lua_pushvalue(L, -2); + lua_rawset(L, 2); /* mark it as loaded */ + return 1; /* return value */ + } + case LUA_ERRFILE: { /* file not found */ + return luaL_error(L, "could not load package `%s' from path `%s'", + lua_tostring(L, 1), getpath(L)); + } + default: { + return luaL_error(L, "error loading package `%s' (%s)", + lua_tostring(L, 1), lua_tostring(L, -1)); + } + } +} + +/* }====================================================== */ + + +static const luaL_reg base_funcs[] = { + {"error", luaB_error}, + {"getmetatable", luaB_getmetatable}, + {"setmetatable", luaB_setmetatable}, + {"getfenv", luaB_getfenv}, + {"setfenv", luaB_setfenv}, + {"next", luaB_next}, + {"ipairs", luaB_ipairs}, + {"pairs", luaB_pairs}, + {"print", luaB_print}, + {"tonumber", luaB_tonumber}, + {"tostring", luaB_tostring}, + {"type", luaB_type}, + {"assert", luaB_assert}, + {"unpack", luaB_unpack}, + {"rawequal", luaB_rawequal}, + {"rawget", luaB_rawget}, + {"rawset", luaB_rawset}, + {"pcall", luaB_pcall}, + {"xpcall", luaB_xpcall}, + {"collectgarbage", luaB_collectgarbage}, + {"gcinfo", luaB_gcinfo}, + {"loadfile", luaB_loadfile}, + {"dofile", luaB_dofile}, + {"loadstring", luaB_loadstring}, + {"require", luaB_require}, + {NULL, NULL} +}; + + +/* +** {====================================================== +** Coroutine library +** ======================================================= +*/ + +static int auxresume (lua_State *L, lua_State *co, int narg) { + int status; + if (!lua_checkstack(co, narg)) + luaL_error(L, "too many arguments to resume"); + lua_xmove(L, co, narg); + status = lua_resume(co, narg); + if (status == 0) { + int nres = lua_gettop(co); + if (!lua_checkstack(L, nres)) + luaL_error(L, "too many results to resume"); + lua_xmove(co, L, nres); /* move yielded values */ + return nres; + } + else { + lua_xmove(co, L, 1); /* move error message */ + return -1; /* error flag */ + } +} + + +static int luaB_coresume (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + int r; + luaL_argcheck(L, co, 1, "coroutine expected"); + r = auxresume(L, co, lua_gettop(L) - 1); + if (r < 0) { + lua_pushboolean(L, 0); + lua_insert(L, -2); + return 2; /* return false + error message */ + } + else { + lua_pushboolean(L, 1); + lua_insert(L, -(r + 1)); + return r + 1; /* return true + `resume' returns */ + } +} + + +static int luaB_auxwrap (lua_State *L) { + lua_State *co = lua_tothread(L, lua_upvalueindex(1)); + int r = auxresume(L, co, lua_gettop(L)); + if (r < 0) { + if (lua_isstring(L, -1)) { /* error object is a string? */ + luaL_where(L, 1); /* add extra info */ + lua_insert(L, -2); + lua_concat(L, 2); + } + lua_error(L); /* propagate error */ + } + return r; +} + + +static int luaB_cocreate (lua_State *L) { + lua_State *NL = lua_newthread(L); + luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, + "Lua function expected"); + lua_pushvalue(L, 1); /* move function to top */ + lua_xmove(L, NL, 1); /* move function from L to NL */ + return 1; +} + + +static int luaB_cowrap (lua_State *L) { + luaB_cocreate(L); + lua_pushcclosure(L, luaB_auxwrap, 1); + return 1; +} + + +static int luaB_yield (lua_State *L) { + return lua_yield(L, lua_gettop(L)); +} + + +static int luaB_costatus (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + luaL_argcheck(L, co, 1, "coroutine expected"); + if (L == co) lua_pushliteral(L, "running"); + else { + lua_Debug ar; + if (lua_getstack(co, 0, &ar) == 0 && lua_gettop(co) == 0) + lua_pushliteral(L, "dead"); + else + lua_pushliteral(L, "suspended"); + } + return 1; +} + + +static const luaL_reg co_funcs[] = { + {"create", luaB_cocreate}, + {"wrap", luaB_cowrap}, + {"resume", luaB_coresume}, + {"yield", luaB_yield}, + {"status", luaB_costatus}, + {NULL, NULL} +}; + +/* }====================================================== */ + + + +static void base_open (lua_State *L) { + lua_pushliteral(L, "_G"); + lua_pushvalue(L, LUA_GLOBALSINDEX); + luaL_openlib(L, NULL, base_funcs, 0); /* open lib into global table */ + lua_pushliteral(L, "_VERSION"); + lua_pushliteral(L, LUA_VERSION); + lua_rawset(L, -3); /* set global _VERSION */ + /* `newproxy' needs a weaktable as upvalue */ + lua_pushliteral(L, "newproxy"); + lua_newtable(L); /* new table `w' */ + lua_pushvalue(L, -1); /* `w' will be its own metatable */ + lua_setmetatable(L, -2); + lua_pushliteral(L, "__mode"); + lua_pushliteral(L, "k"); + lua_rawset(L, -3); /* metatable(w).__mode = "k" */ + lua_pushcclosure(L, luaB_newproxy, 1); + lua_rawset(L, -3); /* set global `newproxy' */ + lua_rawset(L, -1); /* set global _G */ +} + + +LUALIB_API int luaopen_base (lua_State *L) { + base_open(L); + luaL_openlib(L, LUA_COLIBNAME, co_funcs, 0); + lua_newtable(L); + lua_setglobal(L, REQTAB); + return 0; +} + diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ldblib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ldblib.c index 55d4d042c7..8424a37847 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ldblib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ldblib.c @@ -1,309 +1,309 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include -#include -#include - -#define ldblib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - - -static void settabss (lua_State *L, const char *i, const char *v) { - lua_pushstring(L, i); - lua_pushstring(L, v); - lua_rawset(L, -3); -} - - -static void settabsi (lua_State *L, const char *i, int v) { - lua_pushstring(L, i); - lua_pushnumber(L, (lua_Number)v); - lua_rawset(L, -3); -} - - -static int getinfo (lua_State *L) { - lua_Debug ar; - const char *options = luaL_optstring(L, 2, "flnSu"); - if (lua_isnumber(L, 1)) { - if (!lua_getstack(L, (int)(lua_tonumber(L, 1)), &ar)) { - lua_pushnil(L); /* level out of range */ - return 1; - } - } - else if (lua_isfunction(L, 1)) { - lua_pushfstring(L, ">%s", options); - options = lua_tostring(L, -1); - lua_pushvalue(L, 1); - } - else - return luaL_argerror(L, 1, "function or level expected"); - if (!lua_getinfo(L, options, &ar)) - return luaL_argerror(L, 2, "invalid option"); - lua_newtable(L); - for (; *options; options++) { - switch (*options) { - case 'S': - settabss(L, "source", ar.source); - settabss(L, "short_src", ar.short_src); - settabsi(L, "linedefined", ar.linedefined); - settabss(L, "what", ar.what); - break; - case 'l': - settabsi(L, "currentline", ar.currentline); - break; - case 'u': - settabsi(L, "nups", ar.nups); - break; - case 'n': - settabss(L, "name", ar.name); - settabss(L, "namewhat", ar.namewhat); - break; - case 'f': - lua_pushliteral(L, "func"); - lua_pushvalue(L, -3); - lua_rawset(L, -3); - break; - } - } - return 1; /* return table */ -} - - -static int getlocal (lua_State *L) { - lua_Debug ar; - const char *name; - if (!lua_getstack(L, luaL_checkint(L, 1), &ar)) /* level out of range? */ - return luaL_argerror(L, 1, "level out of range"); - name = lua_getlocal(L, &ar, luaL_checkint(L, 2)); - if (name) { - lua_pushstring(L, name); - lua_pushvalue(L, -2); - return 2; - } - else { - lua_pushnil(L); - return 1; - } -} - - -static int setlocal (lua_State *L) { - lua_Debug ar; - if (!lua_getstack(L, luaL_checkint(L, 1), &ar)) /* level out of range? */ - return luaL_argerror(L, 1, "level out of range"); - luaL_checkany(L, 3); - lua_pushstring(L, lua_setlocal(L, &ar, luaL_checkint(L, 2))); - return 1; -} - - -static int auxupvalue (lua_State *L, int get) { - const char *name; - int n = luaL_checkint(L, 2); - luaL_checktype(L, 1, LUA_TFUNCTION); - if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */ - name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); - if (name == NULL) return 0; - lua_pushstring(L, name); - lua_insert(L, -(get+1)); - return get + 1; -} - - -static int getupvalue (lua_State *L) { - return auxupvalue(L, 1); -} - - -static int setupvalue (lua_State *L) { - luaL_checkany(L, 3); - return auxupvalue(L, 0); -} - - - -static const char KEY_HOOK = 'h'; - - -static void hookf (lua_State *L, lua_Debug *ar) { - static const char *const hooknames[] = - {"call", "return", "line", "count", "tail return"}; - lua_pushlightuserdata(L, (void *)&KEY_HOOK); - lua_rawget(L, LUA_REGISTRYINDEX); - if (lua_isfunction(L, -1)) { - lua_pushstring(L, hooknames[(int)ar->event]); - if (ar->currentline >= 0) - lua_pushnumber(L, (lua_Number)ar->currentline); - else lua_pushnil(L); - lua_assert(lua_getinfo(L, "lS", ar)); - lua_call(L, 2, 0); - } - else - lua_pop(L, 1); /* pop result from gettable */ -} - - -static int makemask (const char *smask, int count) { - int mask = 0; - if (strchr(smask, 'c')) mask |= LUA_MASKCALL; - if (strchr(smask, 'r')) mask |= LUA_MASKRET; - if (strchr(smask, 'l')) mask |= LUA_MASKLINE; - if (count > 0) mask |= LUA_MASKCOUNT; - return mask; -} - - -static char *unmakemask (int mask, char *smask) { - int i = 0; - if (mask & LUA_MASKCALL) smask[i++] = 'c'; - if (mask & LUA_MASKRET) smask[i++] = 'r'; - if (mask & LUA_MASKLINE) smask[i++] = 'l'; - smask[i] = '\0'; - return smask; -} - - -static int sethook (lua_State *L) { - if (lua_isnoneornil(L, 1)) { - lua_settop(L, 1); - lua_sethook(L, NULL, 0, 0); /* turn off hooks */ - } - else { - const char *smask = luaL_checkstring(L, 2); - int count = luaL_optint(L, 3, 0); - luaL_checktype(L, 1, LUA_TFUNCTION); - lua_sethook(L, hookf, makemask(smask, count), count); - } - lua_pushlightuserdata(L, (void *)&KEY_HOOK); - lua_pushvalue(L, 1); - lua_rawset(L, LUA_REGISTRYINDEX); /* set new hook */ - return 0; -} - - -static int gethook (lua_State *L) { - char buff[5]; - int mask = lua_gethookmask(L); - lua_Hook hook = lua_gethook(L); - if (hook != NULL && hook != hookf) /* external hook? */ - lua_pushliteral(L, "external hook"); - else { - lua_pushlightuserdata(L, (void *)&KEY_HOOK); - lua_rawget(L, LUA_REGISTRYINDEX); /* get hook */ - } - lua_pushstring(L, unmakemask(mask, buff)); - lua_pushnumber(L, (lua_Number)lua_gethookcount(L)); - return 3; -} - - -static int debug (lua_State *L) { - for (;;) { - char buffer[250]; - fputs("lua_debug> ", stderr); - if (fgets(buffer, sizeof(buffer), stdin) == 0 || - strcmp(buffer, "cont\n") == 0) - return 0; - lua_dostring(L, buffer); - lua_settop(L, 0); /* remove eventual returns */ - } -} - - -#define LEVELS1 12 /* size of the first part of the stack */ -#define LEVELS2 10 /* size of the second part of the stack */ - -static int errorfb (lua_State *L) { - int level = 1; /* skip level 0 (it's this function) */ - int firstpart = 1; /* still before eventual `...' */ - lua_Debug ar; - if (lua_gettop(L) == 0) - lua_pushliteral(L, ""); - else if (!lua_isstring(L, 1)) return 1; /* no string message */ - else lua_pushliteral(L, "\n"); - lua_pushliteral(L, "stack traceback:"); - while (lua_getstack(L, level++, &ar)) { - if (level > LEVELS1 && firstpart) { - /* no more than `LEVELS2' more levels? */ - if (!lua_getstack(L, level+LEVELS2, &ar)) - level--; /* keep going */ - else { - lua_pushliteral(L, "\n\t..."); /* too many levels */ - while (lua_getstack(L, level+LEVELS2, &ar)) /* find last levels */ - level++; - } - firstpart = 0; - continue; - } - lua_pushliteral(L, "\n\t"); - lua_getinfo(L, "Snl", &ar); - lua_pushfstring(L, "%s:", ar.short_src); - if (ar.currentline > 0) - lua_pushfstring(L, "%d:", ar.currentline); - switch (*ar.namewhat) { - case 'g': /* global */ - case 'l': /* local */ - case 'f': /* field */ - case 'm': /* method */ - lua_pushfstring(L, " in function `%s'", ar.name); - break; - default: { - if (*ar.what == 'm') /* main? */ - lua_pushfstring(L, " in main chunk"); - else if (*ar.what == 'C' || *ar.what == 't') - lua_pushliteral(L, " ?"); /* C function or tail call */ - else - lua_pushfstring(L, " in function <%s:%d>", - ar.short_src, ar.linedefined); - } - } - lua_concat(L, lua_gettop(L)); - } - lua_concat(L, lua_gettop(L)); - return 1; -} - - -static const luaL_reg dblib[] = { - {"getlocal", getlocal}, - {"getinfo", getinfo}, - {"gethook", gethook}, - {"getupvalue", getupvalue}, - {"sethook", sethook}, - {"setlocal", setlocal}, - {"setupvalue", setupvalue}, - {"debug", debug}, - {"traceback", errorfb}, - {NULL, NULL} -}; - - -LUALIB_API int luaopen_debug (lua_State *L) { - luaL_openlib(L, LUA_DBLIBNAME, dblib, 0); - lua_pushliteral(L, "_TRACEBACK"); - lua_pushcfunction(L, errorfb); - lua_settable(L, LUA_GLOBALSINDEX); - return 1; -} - +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include +#include +#include + +#define ldblib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + +static void settabss (lua_State *L, const char *i, const char *v) { + lua_pushstring(L, i); + lua_pushstring(L, v); + lua_rawset(L, -3); +} + + +static void settabsi (lua_State *L, const char *i, int v) { + lua_pushstring(L, i); + lua_pushnumber(L, (lua_Number)v); + lua_rawset(L, -3); +} + + +static int getinfo (lua_State *L) { + lua_Debug ar; + const char *options = luaL_optstring(L, 2, "flnSu"); + if (lua_isnumber(L, 1)) { + if (!lua_getstack(L, (int)(lua_tonumber(L, 1)), &ar)) { + lua_pushnil(L); /* level out of range */ + return 1; + } + } + else if (lua_isfunction(L, 1)) { + lua_pushfstring(L, ">%s", options); + options = lua_tostring(L, -1); + lua_pushvalue(L, 1); + } + else + return luaL_argerror(L, 1, "function or level expected"); + if (!lua_getinfo(L, options, &ar)) + return luaL_argerror(L, 2, "invalid option"); + lua_newtable(L); + for (; *options; options++) { + switch (*options) { + case 'S': + settabss(L, "source", ar.source); + settabss(L, "short_src", ar.short_src); + settabsi(L, "linedefined", ar.linedefined); + settabss(L, "what", ar.what); + break; + case 'l': + settabsi(L, "currentline", ar.currentline); + break; + case 'u': + settabsi(L, "nups", ar.nups); + break; + case 'n': + settabss(L, "name", ar.name); + settabss(L, "namewhat", ar.namewhat); + break; + case 'f': + lua_pushliteral(L, "func"); + lua_pushvalue(L, -3); + lua_rawset(L, -3); + break; + } + } + return 1; /* return table */ +} + + +static int getlocal (lua_State *L) { + lua_Debug ar; + const char *name; + if (!lua_getstack(L, luaL_checkint(L, 1), &ar)) /* level out of range? */ + return luaL_argerror(L, 1, "level out of range"); + name = lua_getlocal(L, &ar, luaL_checkint(L, 2)); + if (name) { + lua_pushstring(L, name); + lua_pushvalue(L, -2); + return 2; + } + else { + lua_pushnil(L); + return 1; + } +} + + +static int setlocal (lua_State *L) { + lua_Debug ar; + if (!lua_getstack(L, luaL_checkint(L, 1), &ar)) /* level out of range? */ + return luaL_argerror(L, 1, "level out of range"); + luaL_checkany(L, 3); + lua_pushstring(L, lua_setlocal(L, &ar, luaL_checkint(L, 2))); + return 1; +} + + +static int auxupvalue (lua_State *L, int get) { + const char *name; + int n = luaL_checkint(L, 2); + luaL_checktype(L, 1, LUA_TFUNCTION); + if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */ + name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); + if (name == NULL) return 0; + lua_pushstring(L, name); + lua_insert(L, -(get+1)); + return get + 1; +} + + +static int getupvalue (lua_State *L) { + return auxupvalue(L, 1); +} + + +static int setupvalue (lua_State *L) { + luaL_checkany(L, 3); + return auxupvalue(L, 0); +} + + + +static const char KEY_HOOK = 'h'; + + +static void hookf (lua_State *L, lua_Debug *ar) { + static const char *const hooknames[] = + {"call", "return", "line", "count", "tail return"}; + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_rawget(L, LUA_REGISTRYINDEX); + if (lua_isfunction(L, -1)) { + lua_pushstring(L, hooknames[(int)ar->event]); + if (ar->currentline >= 0) + lua_pushnumber(L, (lua_Number)ar->currentline); + else lua_pushnil(L); + lua_assert(lua_getinfo(L, "lS", ar)); + lua_call(L, 2, 0); + } + else + lua_pop(L, 1); /* pop result from gettable */ +} + + +static int makemask (const char *smask, int count) { + int mask = 0; + if (strchr(smask, 'c')) mask |= LUA_MASKCALL; + if (strchr(smask, 'r')) mask |= LUA_MASKRET; + if (strchr(smask, 'l')) mask |= LUA_MASKLINE; + if (count > 0) mask |= LUA_MASKCOUNT; + return mask; +} + + +static char *unmakemask (int mask, char *smask) { + int i = 0; + if (mask & LUA_MASKCALL) smask[i++] = 'c'; + if (mask & LUA_MASKRET) smask[i++] = 'r'; + if (mask & LUA_MASKLINE) smask[i++] = 'l'; + smask[i] = '\0'; + return smask; +} + + +static int sethook (lua_State *L) { + if (lua_isnoneornil(L, 1)) { + lua_settop(L, 1); + lua_sethook(L, NULL, 0, 0); /* turn off hooks */ + } + else { + const char *smask = luaL_checkstring(L, 2); + int count = luaL_optint(L, 3, 0); + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_sethook(L, hookf, makemask(smask, count), count); + } + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_pushvalue(L, 1); + lua_rawset(L, LUA_REGISTRYINDEX); /* set new hook */ + return 0; +} + + +static int gethook (lua_State *L) { + char buff[5]; + int mask = lua_gethookmask(L); + lua_Hook hook = lua_gethook(L); + if (hook != NULL && hook != hookf) /* external hook? */ + lua_pushliteral(L, "external hook"); + else { + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_rawget(L, LUA_REGISTRYINDEX); /* get hook */ + } + lua_pushstring(L, unmakemask(mask, buff)); + lua_pushnumber(L, (lua_Number)lua_gethookcount(L)); + return 3; +} + + +static int debug (lua_State *L) { + for (;;) { + char buffer[250]; + fputs("lua_debug> ", stderr); + if (fgets(buffer, sizeof(buffer), stdin) == 0 || + strcmp(buffer, "cont\n") == 0) + return 0; + lua_dostring(L, buffer); + lua_settop(L, 0); /* remove eventual returns */ + } +} + + +#define LEVELS1 12 /* size of the first part of the stack */ +#define LEVELS2 10 /* size of the second part of the stack */ + +static int errorfb (lua_State *L) { + int level = 1; /* skip level 0 (it's this function) */ + int firstpart = 1; /* still before eventual `...' */ + lua_Debug ar; + if (lua_gettop(L) == 0) + lua_pushliteral(L, ""); + else if (!lua_isstring(L, 1)) return 1; /* no string message */ + else lua_pushliteral(L, "\n"); + lua_pushliteral(L, "stack traceback:"); + while (lua_getstack(L, level++, &ar)) { + if (level > LEVELS1 && firstpart) { + /* no more than `LEVELS2' more levels? */ + if (!lua_getstack(L, level+LEVELS2, &ar)) + level--; /* keep going */ + else { + lua_pushliteral(L, "\n\t..."); /* too many levels */ + while (lua_getstack(L, level+LEVELS2, &ar)) /* find last levels */ + level++; + } + firstpart = 0; + continue; + } + lua_pushliteral(L, "\n\t"); + lua_getinfo(L, "Snl", &ar); + lua_pushfstring(L, "%s:", ar.short_src); + if (ar.currentline > 0) + lua_pushfstring(L, "%d:", ar.currentline); + switch (*ar.namewhat) { + case 'g': /* global */ + case 'l': /* local */ + case 'f': /* field */ + case 'm': /* method */ + lua_pushfstring(L, " in function `%s'", ar.name); + break; + default: { + if (*ar.what == 'm') /* main? */ + lua_pushfstring(L, " in main chunk"); + else if (*ar.what == 'C' || *ar.what == 't') + lua_pushliteral(L, " ?"); /* C function or tail call */ + else + lua_pushfstring(L, " in function <%s:%d>", + ar.short_src, ar.linedefined); + } + } + lua_concat(L, lua_gettop(L)); + } + lua_concat(L, lua_gettop(L)); + return 1; +} + + +static const luaL_reg dblib[] = { + {"getlocal", getlocal}, + {"getinfo", getinfo}, + {"gethook", gethook}, + {"getupvalue", getupvalue}, + {"sethook", sethook}, + {"setlocal", setlocal}, + {"setupvalue", setupvalue}, + {"debug", debug}, + {"traceback", errorfb}, + {NULL, NULL} +}; + + +LUALIB_API int luaopen_debug (lua_State *L) { + luaL_openlib(L, LUA_DBLIBNAME, dblib, 0); + lua_pushliteral(L, "_TRACEBACK"); + lua_pushcfunction(L, errorfb); + lua_settable(L, LUA_GLOBALSINDEX); + return 1; +} + diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/liolib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/liolib.c index 01281e2733..1aa0cc5bc3 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/liolib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/liolib.c @@ -1,760 +1,760 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include -#include -#include -#include -#include -#include - -#define liolib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - - -/* -** by default, gcc does not get `tmpname' -*/ -#ifndef USE_TMPNAME -#ifdef __GNUC__ -#define USE_TMPNAME 0 -#else -#define USE_TMPNAME 1 -#endif -#endif - - -/* -** by default, posix systems get `popen' -*/ -#ifndef USE_POPEN -#ifdef _POSIX_C_SOURCE -#if _POSIX_C_SOURCE >= 2 -#define USE_POPEN 1 -#endif -#endif -#endif - -#ifndef USE_POPEN -#define USE_POPEN 0 -#endif - - - - -/* -** {====================================================== -** FILE Operations -** ======================================================= -*/ - - -#if !USE_POPEN -#define pclose(f) (-1) -#endif - - -#define FILEHANDLE "FILE*" - -#define IO_INPUT "_input" -#define IO_OUTPUT "_output" - - -static int pushresult (lua_State *L, int i, const char *filename) { - if (i) { - lua_pushboolean(L, 1); - return 1; - } - else { - lua_pushnil(L); - if (filename) - lua_pushfstring(L, "%s: %s", filename, strerror(errno)); - else - lua_pushfstring(L, "%s", strerror(errno)); - lua_pushnumber(L, errno); - return 3; - } -} - - -static FILE **topfile (lua_State *L, int findex) { - FILE **f = (FILE **)luaL_checkudata(L, findex, FILEHANDLE); - if (f == NULL) luaL_argerror(L, findex, "bad file"); - return f; -} - - -static int io_type (lua_State *L) { - FILE **f = (FILE **)luaL_checkudata(L, 1, FILEHANDLE); - if (f == NULL) lua_pushnil(L); - else if (*f == NULL) - lua_pushliteral(L, "closed file"); - else - lua_pushliteral(L, "file"); - return 1; -} - - -static FILE *tofile (lua_State *L, int findex) { - FILE **f = topfile(L, findex); - if (*f == NULL) - luaL_error(L, "attempt to use a closed file"); - return *f; -} - - - -/* -** When creating file handles, always creates a `closed' file handle -** before opening the actual file; so, if there is a memory error, the -** file is not left opened. -*/ -static FILE **newfile (lua_State *L) { - FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *)); - *pf = NULL; /* file handle is currently `closed' */ - luaL_getmetatable(L, FILEHANDLE); - lua_setmetatable(L, -2); - return pf; -} - - -/* -** assumes that top of the stack is the `io' library, and next is -** the `io' metatable -*/ -static void registerfile (lua_State *L, FILE *f, const char *name, - const char *impname) { - lua_pushstring(L, name); - *newfile(L) = f; - if (impname) { - lua_pushstring(L, impname); - lua_pushvalue(L, -2); - lua_settable(L, -6); /* metatable[impname] = file */ - } - lua_settable(L, -3); /* io[name] = file */ -} - - -static int aux_close (lua_State *L) { - FILE *f = tofile(L, 1); - if (f == stdin || f == stdout || f == stderr) - return 0; /* file cannot be closed */ - else { - int ok = (pclose(f) != -1) || (fclose(f) == 0); - if (ok) - *(FILE **)lua_touserdata(L, 1) = NULL; /* mark file as closed */ - return ok; - } -} - - -static int io_close (lua_State *L) { - if (lua_isnone(L, 1)) { - lua_pushstring(L, IO_OUTPUT); - lua_rawget(L, lua_upvalueindex(1)); - } - return pushresult(L, aux_close(L), NULL); -} - - -static int io_gc (lua_State *L) { - FILE **f = topfile(L, 1); - if (*f != NULL) /* ignore closed files */ - aux_close(L); - return 0; -} - - -static int io_tostring (lua_State *L) { - char buff[32]; - FILE **f = topfile(L, 1); - if (*f == NULL) - strcpy(buff, "closed"); - else - sprintf(buff, "%p", lua_touserdata(L, 1)); - lua_pushfstring(L, "file (%s)", buff); - return 1; -} - - -static int io_open (lua_State *L) { - const char *filename = luaL_checkstring(L, 1); - const char *mode = luaL_optstring(L, 2, "r"); - FILE **pf = newfile(L); - *pf = fopen(filename, mode); - return (*pf == NULL) ? pushresult(L, 0, filename) : 1; -} - - -static int io_popen (lua_State *L) { -#if !USE_POPEN - luaL_error(L, "`popen' not supported"); - return 0; -#else - const char *filename = luaL_checkstring(L, 1); - const char *mode = luaL_optstring(L, 2, "r"); - FILE **pf = newfile(L); - *pf = popen(filename, mode); - return (*pf == NULL) ? pushresult(L, 0, filename) : 1; -#endif -} - - -static int io_tmpfile (lua_State *L) { - FILE **pf = newfile(L); - *pf = tmpfile(); - return (*pf == NULL) ? pushresult(L, 0, NULL) : 1; -} - - -static FILE *getiofile (lua_State *L, const char *name) { - lua_pushstring(L, name); - lua_rawget(L, lua_upvalueindex(1)); - return tofile(L, -1); -} - - -static int g_iofile (lua_State *L, const char *name, const char *mode) { - if (!lua_isnoneornil(L, 1)) { - const char *filename = lua_tostring(L, 1); - lua_pushstring(L, name); - if (filename) { - FILE **pf = newfile(L); - *pf = fopen(filename, mode); - if (*pf == NULL) { - lua_pushfstring(L, "%s: %s", filename, strerror(errno)); - luaL_argerror(L, 1, lua_tostring(L, -1)); - } - } - else { - tofile(L, 1); /* check that it's a valid file handle */ - lua_pushvalue(L, 1); - } - lua_rawset(L, lua_upvalueindex(1)); - } - /* return current value */ - lua_pushstring(L, name); - lua_rawget(L, lua_upvalueindex(1)); - return 1; -} - - -static int io_input (lua_State *L) { - return g_iofile(L, IO_INPUT, "r"); -} - - -static int io_output (lua_State *L) { - return g_iofile(L, IO_OUTPUT, "w"); -} - - -static int io_readline (lua_State *L); - - -static void aux_lines (lua_State *L, int idx, int close) { - lua_pushliteral(L, FILEHANDLE); - lua_rawget(L, LUA_REGISTRYINDEX); - lua_pushvalue(L, idx); - lua_pushboolean(L, close); /* close/not close file when finished */ - lua_pushcclosure(L, io_readline, 3); -} - - -static int f_lines (lua_State *L) { - tofile(L, 1); /* check that it's a valid file handle */ - aux_lines(L, 1, 0); - return 1; -} - - -static int io_lines (lua_State *L) { - if (lua_isnoneornil(L, 1)) { /* no arguments? */ - lua_pushstring(L, IO_INPUT); - lua_rawget(L, lua_upvalueindex(1)); /* will iterate over default input */ - return f_lines(L); - } - else { - const char *filename = luaL_checkstring(L, 1); - FILE **pf = newfile(L); - *pf = fopen(filename, "r"); - luaL_argcheck(L, *pf, 1, strerror(errno)); - aux_lines(L, lua_gettop(L), 1); - return 1; - } -} - - -/* -** {====================================================== -** READ -** ======================================================= -*/ - - -static int read_number (lua_State *L, FILE *f) { - lua_Number d; - if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { - lua_pushnumber(L, d); - return 1; - } - else return 0; /* read fails */ -} - - -static int test_eof (lua_State *L, FILE *f) { - int c = getc(f); - ungetc(c, f); - lua_pushlstring(L, NULL, 0); - return (c != EOF); -} - - -static int read_line (lua_State *L, FILE *f) { - luaL_Buffer b; - luaL_buffinit(L, &b); - for (;;) { - size_t l; - char *p = luaL_prepbuffer(&b); - if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ - luaL_pushresult(&b); /* close buffer */ - return (lua_strlen(L, -1) > 0); /* check whether read something */ - } - l = strlen(p); - if (p[l-1] != '\n') - luaL_addsize(&b, l); - else { - luaL_addsize(&b, l - 1); /* do not include `eol' */ - luaL_pushresult(&b); /* close buffer */ - return 1; /* read at least an `eol' */ - } - } -} - - -static int read_chars (lua_State *L, FILE *f, size_t n) { - size_t rlen; /* how much to read */ - size_t nr; /* number of chars actually read */ - luaL_Buffer b; - luaL_buffinit(L, &b); - rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ - do { - char *p = luaL_prepbuffer(&b); - if (rlen > n) rlen = n; /* cannot read more than asked */ - nr = fread(p, sizeof(char), rlen, f); - luaL_addsize(&b, nr); - n -= nr; /* still have to read `n' chars */ - } while (n > 0 && nr == rlen); /* until end of count or eof */ - luaL_pushresult(&b); /* close buffer */ - return (n == 0 || lua_strlen(L, -1) > 0); -} - - -static int g_read (lua_State *L, FILE *f, int first) { - int nargs = lua_gettop(L) - 1; - int success; - int n; - if (nargs == 0) { /* no arguments? */ - success = read_line(L, f); - n = first+1; /* to return 1 result */ - } - else { /* ensure stack space for all results and for auxlib's buffer */ - luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); - success = 1; - for (n = first; nargs-- && success; n++) { - if (lua_type(L, n) == LUA_TNUMBER) { - size_t l = (size_t)lua_tonumber(L, n); - success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); - } - else { - const char *p = lua_tostring(L, n); - luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); - switch (p[1]) { - case 'n': /* number */ - success = read_number(L, f); - break; - case 'l': /* line */ - success = read_line(L, f); - break; - case 'a': /* file */ - read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */ - success = 1; /* always success */ - break; - case 'w': /* word */ - return luaL_error(L, "obsolete option `*w' to `read'"); - default: - return luaL_argerror(L, n, "invalid format"); - } - } - } - } - if (!success) { - lua_pop(L, 1); /* remove last result */ - lua_pushnil(L); /* push nil instead */ - } - return n - first; -} - - -static int io_read (lua_State *L) { - return g_read(L, getiofile(L, IO_INPUT), 1); -} - - -static int f_read (lua_State *L) { - return g_read(L, tofile(L, 1), 2); -} - - -static int io_readline (lua_State *L) { - FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(2)); - if (f == NULL) /* file is already closed? */ - luaL_error(L, "file is already closed"); - if (read_line(L, f)) return 1; - else { /* EOF */ - if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */ - lua_settop(L, 0); - lua_pushvalue(L, lua_upvalueindex(2)); - aux_close(L); /* close it */ - } - return 0; - } -} - -/* }====================================================== */ - - -static int g_write (lua_State *L, FILE *f, int arg) { - int nargs = lua_gettop(L) - 1; - int status = 1; - for (; nargs--; arg++) { - if (lua_type(L, arg) == LUA_TNUMBER) { - /* optimization: could be done exactly as for strings */ - status = status && - fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; - } - else { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); - status = status && (fwrite(s, sizeof(char), l, f) == l); - } - } - return pushresult(L, status, NULL); -} - - -static int io_write (lua_State *L) { - return g_write(L, getiofile(L, IO_OUTPUT), 1); -} - - -static int f_write (lua_State *L) { - return g_write(L, tofile(L, 1), 2); -} - - -static int f_seek (lua_State *L) { - static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; - static const char *const modenames[] = {"set", "cur", "end", NULL}; - FILE *f = tofile(L, 1); - int op = luaL_findstring(luaL_optstring(L, 2, "cur"), modenames); - long offset = luaL_optlong(L, 3, 0); - luaL_argcheck(L, op != -1, 2, "invalid mode"); - op = fseek(f, offset, mode[op]); - if (op) - return pushresult(L, 0, NULL); /* error */ - else { - lua_pushnumber(L, ftell(f)); - return 1; - } -} - - -static int io_flush (lua_State *L) { - return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); -} - - -static int f_flush (lua_State *L) { - return pushresult(L, fflush(tofile(L, 1)) == 0, NULL); -} - - -static const luaL_reg iolib[] = { - {"input", io_input}, - {"output", io_output}, - {"lines", io_lines}, - {"close", io_close}, - {"flush", io_flush}, - {"open", io_open}, - {"popen", io_popen}, - {"read", io_read}, - {"tmpfile", io_tmpfile}, - {"type", io_type}, - {"write", io_write}, - {NULL, NULL} -}; - - -static const luaL_reg flib[] = { - {"flush", f_flush}, - {"read", f_read}, - {"lines", f_lines}, - {"seek", f_seek}, - {"write", f_write}, - {"close", io_close}, - {"__gc", io_gc}, - {"__tostring", io_tostring}, - {NULL, NULL} -}; - - -static void createmeta (lua_State *L) { - luaL_newmetatable(L, FILEHANDLE); /* create new metatable for file handles */ - /* file methods */ - lua_pushliteral(L, "__index"); - lua_pushvalue(L, -2); /* push metatable */ - lua_rawset(L, -3); /* metatable.__index = metatable */ - luaL_openlib(L, NULL, flib, 0); -} - -/* }====================================================== */ - - -/* -** {====================================================== -** Other O.S. Operations -** ======================================================= -*/ - -static int io_execute (lua_State *L) { - lua_pushnumber(L, system(luaL_checkstring(L, 1))); - return 1; -} - - -static int io_remove (lua_State *L) { - const char *filename = luaL_checkstring(L, 1); - return pushresult(L, remove(filename) == 0, filename); -} - - -static int io_rename (lua_State *L) { - const char *fromname = luaL_checkstring(L, 1); - const char *toname = luaL_checkstring(L, 2); - return pushresult(L, rename(fromname, toname) == 0, fromname); -} - - -static int io_tmpname (lua_State *L) { -#if !USE_TMPNAME - luaL_error(L, "`tmpname' not supported"); - return 0; -#else - char buff[L_tmpnam]; - if (tmpnam(buff) != buff) - return luaL_error(L, "unable to generate a unique filename in `tmpname'"); - lua_pushstring(L, buff); - return 1; -#endif -} - - -static int io_getenv (lua_State *L) { - lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ - return 1; -} - - -static int io_clock (lua_State *L) { - lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); - return 1; -} - - -/* -** {====================================================== -** Time/Date operations -** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, -** wday=%w+1, yday=%j, isdst=? } -** ======================================================= -*/ - -static void setfield (lua_State *L, const char *key, int value) { - lua_pushstring(L, key); - lua_pushnumber(L, value); - lua_rawset(L, -3); -} - -static void setboolfield (lua_State *L, const char *key, int value) { - lua_pushstring(L, key); - lua_pushboolean(L, value); - lua_rawset(L, -3); -} - -static int getboolfield (lua_State *L, const char *key) { - int res; - lua_pushstring(L, key); - lua_gettable(L, -2); - res = lua_toboolean(L, -1); - lua_pop(L, 1); - return res; -} - - -static int getfield (lua_State *L, const char *key, int d) { - int res; - lua_pushstring(L, key); - lua_gettable(L, -2); - if (lua_isnumber(L, -1)) - res = (int)(lua_tonumber(L, -1)); - else { - if (d == -2) - return luaL_error(L, "field `%s' missing in date table", key); - res = d; - } - lua_pop(L, 1); - return res; -} - - -static int io_date (lua_State *L) { - const char *s = luaL_optstring(L, 1, "%c"); - time_t t = (time_t)(luaL_optnumber(L, 2, -1)); - struct tm *stm; - if (t == (time_t)(-1)) /* no time given? */ - t = time(NULL); /* use current time */ - if (*s == '!') { /* UTC? */ - stm = gmtime(&t); - s++; /* skip `!' */ - } - else - stm = localtime(&t); - if (stm == NULL) /* invalid date? */ - lua_pushnil(L); - else if (strcmp(s, "*t") == 0) { - lua_newtable(L); - setfield(L, "sec", stm->tm_sec); - setfield(L, "min", stm->tm_min); - setfield(L, "hour", stm->tm_hour); - setfield(L, "day", stm->tm_mday); - setfield(L, "month", stm->tm_mon+1); - setfield(L, "year", stm->tm_year+1900); - setfield(L, "wday", stm->tm_wday+1); - setfield(L, "yday", stm->tm_yday+1); - setboolfield(L, "isdst", stm->tm_isdst); - } - else { - char b[256]; - if (strftime(b, sizeof(b), s, stm)) - lua_pushstring(L, b); - else - return luaL_error(L, "`date' format too long"); - } - return 1; -} - - -static int io_time (lua_State *L) { - if (lua_isnoneornil(L, 1)) /* called without args? */ - lua_pushnumber(L, time(NULL)); /* return current time */ - else { - time_t t; - struct tm ts; - luaL_checktype(L, 1, LUA_TTABLE); - lua_settop(L, 1); /* make sure table is at the top */ - ts.tm_sec = getfield(L, "sec", 0); - ts.tm_min = getfield(L, "min", 0); - ts.tm_hour = getfield(L, "hour", 12); - ts.tm_mday = getfield(L, "day", -2); - ts.tm_mon = getfield(L, "month", -2) - 1; - ts.tm_year = getfield(L, "year", -2) - 1900; - ts.tm_isdst = getboolfield(L, "isdst"); - t = mktime(&ts); - if (t == (time_t)(-1)) - lua_pushnil(L); - else - lua_pushnumber(L, t); - } - return 1; -} - - -static int io_difftime (lua_State *L) { - lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), - (time_t)(luaL_optnumber(L, 2, 0)))); - return 1; -} - -/* }====================================================== */ - - -static int io_setloc (lua_State *L) { - static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, - LC_NUMERIC, LC_TIME}; - static const char *const catnames[] = {"all", "collate", "ctype", "monetary", - "numeric", "time", NULL}; - const char *l = lua_tostring(L, 1); - int op = luaL_findstring(luaL_optstring(L, 2, "all"), catnames); - luaL_argcheck(L, l || lua_isnoneornil(L, 1), 1, "string expected"); - luaL_argcheck(L, op != -1, 2, "invalid option"); - lua_pushstring(L, setlocale(cat[op], l)); - return 1; -} - - -static int io_exit (lua_State *L) { - exit(luaL_optint(L, 1, EXIT_SUCCESS)); - return 0; /* to avoid warnings */ -} - -static const luaL_reg syslib[] = { - {"clock", io_clock}, - {"date", io_date}, - {"difftime", io_difftime}, - {"execute", io_execute}, - {"exit", io_exit}, - {"getenv", io_getenv}, - {"remove", io_remove}, - {"rename", io_rename}, - {"setlocale", io_setloc}, - {"time", io_time}, - {"tmpname", io_tmpname}, - {NULL, NULL} -}; - -/* }====================================================== */ - - - -LUALIB_API int luaopen_io (lua_State *L) { - luaL_openlib(L, LUA_OSLIBNAME, syslib, 0); - createmeta(L); - lua_pushvalue(L, -1); - luaL_openlib(L, LUA_IOLIBNAME, iolib, 1); - /* put predefined file handles into `io' table */ - registerfile(L, stdin, "stdin", IO_INPUT); - registerfile(L, stdout, "stdout", IO_OUTPUT); - registerfile(L, stderr, "stderr", NULL); - return 1; -} - +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include +#include +#include +#include +#include +#include + +#define liolib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + +/* +** by default, gcc does not get `tmpname' +*/ +#ifndef USE_TMPNAME +#ifdef __GNUC__ +#define USE_TMPNAME 0 +#else +#define USE_TMPNAME 1 +#endif +#endif + + +/* +** by default, posix systems get `popen' +*/ +#ifndef USE_POPEN +#ifdef _POSIX_C_SOURCE +#if _POSIX_C_SOURCE >= 2 +#define USE_POPEN 1 +#endif +#endif +#endif + +#ifndef USE_POPEN +#define USE_POPEN 0 +#endif + + + + +/* +** {====================================================== +** FILE Operations +** ======================================================= +*/ + + +#if !USE_POPEN +#define pclose(f) (-1) +#endif + + +#define FILEHANDLE "FILE*" + +#define IO_INPUT "_input" +#define IO_OUTPUT "_output" + + +static int pushresult (lua_State *L, int i, const char *filename) { + if (i) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushnil(L); + if (filename) + lua_pushfstring(L, "%s: %s", filename, strerror(errno)); + else + lua_pushfstring(L, "%s", strerror(errno)); + lua_pushnumber(L, errno); + return 3; + } +} + + +static FILE **topfile (lua_State *L, int findex) { + FILE **f = (FILE **)luaL_checkudata(L, findex, FILEHANDLE); + if (f == NULL) luaL_argerror(L, findex, "bad file"); + return f; +} + + +static int io_type (lua_State *L) { + FILE **f = (FILE **)luaL_checkudata(L, 1, FILEHANDLE); + if (f == NULL) lua_pushnil(L); + else if (*f == NULL) + lua_pushliteral(L, "closed file"); + else + lua_pushliteral(L, "file"); + return 1; +} + + +static FILE *tofile (lua_State *L, int findex) { + FILE **f = topfile(L, findex); + if (*f == NULL) + luaL_error(L, "attempt to use a closed file"); + return *f; +} + + + +/* +** When creating file handles, always creates a `closed' file handle +** before opening the actual file; so, if there is a memory error, the +** file is not left opened. +*/ +static FILE **newfile (lua_State *L) { + FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *)); + *pf = NULL; /* file handle is currently `closed' */ + luaL_getmetatable(L, FILEHANDLE); + lua_setmetatable(L, -2); + return pf; +} + + +/* +** assumes that top of the stack is the `io' library, and next is +** the `io' metatable +*/ +static void registerfile (lua_State *L, FILE *f, const char *name, + const char *impname) { + lua_pushstring(L, name); + *newfile(L) = f; + if (impname) { + lua_pushstring(L, impname); + lua_pushvalue(L, -2); + lua_settable(L, -6); /* metatable[impname] = file */ + } + lua_settable(L, -3); /* io[name] = file */ +} + + +static int aux_close (lua_State *L) { + FILE *f = tofile(L, 1); + if (f == stdin || f == stdout || f == stderr) + return 0; /* file cannot be closed */ + else { + int ok = (pclose(f) != -1) || (fclose(f) == 0); + if (ok) + *(FILE **)lua_touserdata(L, 1) = NULL; /* mark file as closed */ + return ok; + } +} + + +static int io_close (lua_State *L) { + if (lua_isnone(L, 1)) { + lua_pushstring(L, IO_OUTPUT); + lua_rawget(L, lua_upvalueindex(1)); + } + return pushresult(L, aux_close(L), NULL); +} + + +static int io_gc (lua_State *L) { + FILE **f = topfile(L, 1); + if (*f != NULL) /* ignore closed files */ + aux_close(L); + return 0; +} + + +static int io_tostring (lua_State *L) { + char buff[32]; + FILE **f = topfile(L, 1); + if (*f == NULL) + strcpy(buff, "closed"); + else + sprintf(buff, "%p", lua_touserdata(L, 1)); + lua_pushfstring(L, "file (%s)", buff); + return 1; +} + + +static int io_open (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + FILE **pf = newfile(L); + *pf = fopen(filename, mode); + return (*pf == NULL) ? pushresult(L, 0, filename) : 1; +} + + +static int io_popen (lua_State *L) { +#if !USE_POPEN + luaL_error(L, "`popen' not supported"); + return 0; +#else + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + FILE **pf = newfile(L); + *pf = popen(filename, mode); + return (*pf == NULL) ? pushresult(L, 0, filename) : 1; +#endif +} + + +static int io_tmpfile (lua_State *L) { + FILE **pf = newfile(L); + *pf = tmpfile(); + return (*pf == NULL) ? pushresult(L, 0, NULL) : 1; +} + + +static FILE *getiofile (lua_State *L, const char *name) { + lua_pushstring(L, name); + lua_rawget(L, lua_upvalueindex(1)); + return tofile(L, -1); +} + + +static int g_iofile (lua_State *L, const char *name, const char *mode) { + if (!lua_isnoneornil(L, 1)) { + const char *filename = lua_tostring(L, 1); + lua_pushstring(L, name); + if (filename) { + FILE **pf = newfile(L); + *pf = fopen(filename, mode); + if (*pf == NULL) { + lua_pushfstring(L, "%s: %s", filename, strerror(errno)); + luaL_argerror(L, 1, lua_tostring(L, -1)); + } + } + else { + tofile(L, 1); /* check that it's a valid file handle */ + lua_pushvalue(L, 1); + } + lua_rawset(L, lua_upvalueindex(1)); + } + /* return current value */ + lua_pushstring(L, name); + lua_rawget(L, lua_upvalueindex(1)); + return 1; +} + + +static int io_input (lua_State *L) { + return g_iofile(L, IO_INPUT, "r"); +} + + +static int io_output (lua_State *L) { + return g_iofile(L, IO_OUTPUT, "w"); +} + + +static int io_readline (lua_State *L); + + +static void aux_lines (lua_State *L, int idx, int close) { + lua_pushliteral(L, FILEHANDLE); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushvalue(L, idx); + lua_pushboolean(L, close); /* close/not close file when finished */ + lua_pushcclosure(L, io_readline, 3); +} + + +static int f_lines (lua_State *L) { + tofile(L, 1); /* check that it's a valid file handle */ + aux_lines(L, 1, 0); + return 1; +} + + +static int io_lines (lua_State *L) { + if (lua_isnoneornil(L, 1)) { /* no arguments? */ + lua_pushstring(L, IO_INPUT); + lua_rawget(L, lua_upvalueindex(1)); /* will iterate over default input */ + return f_lines(L); + } + else { + const char *filename = luaL_checkstring(L, 1); + FILE **pf = newfile(L); + *pf = fopen(filename, "r"); + luaL_argcheck(L, *pf, 1, strerror(errno)); + aux_lines(L, lua_gettop(L), 1); + return 1; + } +} + + +/* +** {====================================================== +** READ +** ======================================================= +*/ + + +static int read_number (lua_State *L, FILE *f) { + lua_Number d; + if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { + lua_pushnumber(L, d); + return 1; + } + else return 0; /* read fails */ +} + + +static int test_eof (lua_State *L, FILE *f) { + int c = getc(f); + ungetc(c, f); + lua_pushlstring(L, NULL, 0); + return (c != EOF); +} + + +static int read_line (lua_State *L, FILE *f) { + luaL_Buffer b; + luaL_buffinit(L, &b); + for (;;) { + size_t l; + char *p = luaL_prepbuffer(&b); + if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ + luaL_pushresult(&b); /* close buffer */ + return (lua_strlen(L, -1) > 0); /* check whether read something */ + } + l = strlen(p); + if (p[l-1] != '\n') + luaL_addsize(&b, l); + else { + luaL_addsize(&b, l - 1); /* do not include `eol' */ + luaL_pushresult(&b); /* close buffer */ + return 1; /* read at least an `eol' */ + } + } +} + + +static int read_chars (lua_State *L, FILE *f, size_t n) { + size_t rlen; /* how much to read */ + size_t nr; /* number of chars actually read */ + luaL_Buffer b; + luaL_buffinit(L, &b); + rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ + do { + char *p = luaL_prepbuffer(&b); + if (rlen > n) rlen = n; /* cannot read more than asked */ + nr = fread(p, sizeof(char), rlen, f); + luaL_addsize(&b, nr); + n -= nr; /* still have to read `n' chars */ + } while (n > 0 && nr == rlen); /* until end of count or eof */ + luaL_pushresult(&b); /* close buffer */ + return (n == 0 || lua_strlen(L, -1) > 0); +} + + +static int g_read (lua_State *L, FILE *f, int first) { + int nargs = lua_gettop(L) - 1; + int success; + int n; + if (nargs == 0) { /* no arguments? */ + success = read_line(L, f); + n = first+1; /* to return 1 result */ + } + else { /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + success = 1; + for (n = first; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)lua_tonumber(L, n); + success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); + } + else { + const char *p = lua_tostring(L, n); + luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); + switch (p[1]) { + case 'n': /* number */ + success = read_number(L, f); + break; + case 'l': /* line */ + success = read_line(L, f); + break; + case 'a': /* file */ + read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */ + success = 1; /* always success */ + break; + case 'w': /* word */ + return luaL_error(L, "obsolete option `*w' to `read'"); + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (!success) { + lua_pop(L, 1); /* remove last result */ + lua_pushnil(L); /* push nil instead */ + } + return n - first; +} + + +static int io_read (lua_State *L) { + return g_read(L, getiofile(L, IO_INPUT), 1); +} + + +static int f_read (lua_State *L) { + return g_read(L, tofile(L, 1), 2); +} + + +static int io_readline (lua_State *L) { + FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(2)); + if (f == NULL) /* file is already closed? */ + luaL_error(L, "file is already closed"); + if (read_line(L, f)) return 1; + else { /* EOF */ + if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */ + lua_settop(L, 0); + lua_pushvalue(L, lua_upvalueindex(2)); + aux_close(L); /* close it */ + } + return 0; + } +} + +/* }====================================================== */ + + +static int g_write (lua_State *L, FILE *f, int arg) { + int nargs = lua_gettop(L) - 1; + int status = 1; + for (; nargs--; arg++) { + if (lua_type(L, arg) == LUA_TNUMBER) { + /* optimization: could be done exactly as for strings */ + status = status && + fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; + } + else { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + status = status && (fwrite(s, sizeof(char), l, f) == l); + } + } + return pushresult(L, status, NULL); +} + + +static int io_write (lua_State *L) { + return g_write(L, getiofile(L, IO_OUTPUT), 1); +} + + +static int f_write (lua_State *L) { + return g_write(L, tofile(L, 1), 2); +} + + +static int f_seek (lua_State *L) { + static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; + static const char *const modenames[] = {"set", "cur", "end", NULL}; + FILE *f = tofile(L, 1); + int op = luaL_findstring(luaL_optstring(L, 2, "cur"), modenames); + long offset = luaL_optlong(L, 3, 0); + luaL_argcheck(L, op != -1, 2, "invalid mode"); + op = fseek(f, offset, mode[op]); + if (op) + return pushresult(L, 0, NULL); /* error */ + else { + lua_pushnumber(L, ftell(f)); + return 1; + } +} + + +static int io_flush (lua_State *L) { + return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); +} + + +static int f_flush (lua_State *L) { + return pushresult(L, fflush(tofile(L, 1)) == 0, NULL); +} + + +static const luaL_reg iolib[] = { + {"input", io_input}, + {"output", io_output}, + {"lines", io_lines}, + {"close", io_close}, + {"flush", io_flush}, + {"open", io_open}, + {"popen", io_popen}, + {"read", io_read}, + {"tmpfile", io_tmpfile}, + {"type", io_type}, + {"write", io_write}, + {NULL, NULL} +}; + + +static const luaL_reg flib[] = { + {"flush", f_flush}, + {"read", f_read}, + {"lines", f_lines}, + {"seek", f_seek}, + {"write", f_write}, + {"close", io_close}, + {"__gc", io_gc}, + {"__tostring", io_tostring}, + {NULL, NULL} +}; + + +static void createmeta (lua_State *L) { + luaL_newmetatable(L, FILEHANDLE); /* create new metatable for file handles */ + /* file methods */ + lua_pushliteral(L, "__index"); + lua_pushvalue(L, -2); /* push metatable */ + lua_rawset(L, -3); /* metatable.__index = metatable */ + luaL_openlib(L, NULL, flib, 0); +} + +/* }====================================================== */ + + +/* +** {====================================================== +** Other O.S. Operations +** ======================================================= +*/ + +static int io_execute (lua_State *L) { + lua_pushnumber(L, system(luaL_checkstring(L, 1))); + return 1; +} + + +static int io_remove (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + return pushresult(L, remove(filename) == 0, filename); +} + + +static int io_rename (lua_State *L) { + const char *fromname = luaL_checkstring(L, 1); + const char *toname = luaL_checkstring(L, 2); + return pushresult(L, rename(fromname, toname) == 0, fromname); +} + + +static int io_tmpname (lua_State *L) { +#if !USE_TMPNAME + luaL_error(L, "`tmpname' not supported"); + return 0; +#else + char buff[L_tmpnam]; + if (tmpnam(buff) != buff) + return luaL_error(L, "unable to generate a unique filename in `tmpname'"); + lua_pushstring(L, buff); + return 1; +#endif +} + + +static int io_getenv (lua_State *L) { + lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ + return 1; +} + + +static int io_clock (lua_State *L) { + lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); + return 1; +} + + +/* +** {====================================================== +** Time/Date operations +** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, +** wday=%w+1, yday=%j, isdst=? } +** ======================================================= +*/ + +static void setfield (lua_State *L, const char *key, int value) { + lua_pushstring(L, key); + lua_pushnumber(L, value); + lua_rawset(L, -3); +} + +static void setboolfield (lua_State *L, const char *key, int value) { + lua_pushstring(L, key); + lua_pushboolean(L, value); + lua_rawset(L, -3); +} + +static int getboolfield (lua_State *L, const char *key) { + int res; + lua_pushstring(L, key); + lua_gettable(L, -2); + res = lua_toboolean(L, -1); + lua_pop(L, 1); + return res; +} + + +static int getfield (lua_State *L, const char *key, int d) { + int res; + lua_pushstring(L, key); + lua_gettable(L, -2); + if (lua_isnumber(L, -1)) + res = (int)(lua_tonumber(L, -1)); + else { + if (d == -2) + return luaL_error(L, "field `%s' missing in date table", key); + res = d; + } + lua_pop(L, 1); + return res; +} + + +static int io_date (lua_State *L) { + const char *s = luaL_optstring(L, 1, "%c"); + time_t t = (time_t)(luaL_optnumber(L, 2, -1)); + struct tm *stm; + if (t == (time_t)(-1)) /* no time given? */ + t = time(NULL); /* use current time */ + if (*s == '!') { /* UTC? */ + stm = gmtime(&t); + s++; /* skip `!' */ + } + else + stm = localtime(&t); + if (stm == NULL) /* invalid date? */ + lua_pushnil(L); + else if (strcmp(s, "*t") == 0) { + lua_newtable(L); + setfield(L, "sec", stm->tm_sec); + setfield(L, "min", stm->tm_min); + setfield(L, "hour", stm->tm_hour); + setfield(L, "day", stm->tm_mday); + setfield(L, "month", stm->tm_mon+1); + setfield(L, "year", stm->tm_year+1900); + setfield(L, "wday", stm->tm_wday+1); + setfield(L, "yday", stm->tm_yday+1); + setboolfield(L, "isdst", stm->tm_isdst); + } + else { + char b[256]; + if (strftime(b, sizeof(b), s, stm)) + lua_pushstring(L, b); + else + return luaL_error(L, "`date' format too long"); + } + return 1; +} + + +static int io_time (lua_State *L) { + if (lua_isnoneornil(L, 1)) /* called without args? */ + lua_pushnumber(L, time(NULL)); /* return current time */ + else { + time_t t; + struct tm ts; + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 1); /* make sure table is at the top */ + ts.tm_sec = getfield(L, "sec", 0); + ts.tm_min = getfield(L, "min", 0); + ts.tm_hour = getfield(L, "hour", 12); + ts.tm_mday = getfield(L, "day", -2); + ts.tm_mon = getfield(L, "month", -2) - 1; + ts.tm_year = getfield(L, "year", -2) - 1900; + ts.tm_isdst = getboolfield(L, "isdst"); + t = mktime(&ts); + if (t == (time_t)(-1)) + lua_pushnil(L); + else + lua_pushnumber(L, t); + } + return 1; +} + + +static int io_difftime (lua_State *L) { + lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), + (time_t)(luaL_optnumber(L, 2, 0)))); + return 1; +} + +/* }====================================================== */ + + +static int io_setloc (lua_State *L) { + static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, + LC_NUMERIC, LC_TIME}; + static const char *const catnames[] = {"all", "collate", "ctype", "monetary", + "numeric", "time", NULL}; + const char *l = lua_tostring(L, 1); + int op = luaL_findstring(luaL_optstring(L, 2, "all"), catnames); + luaL_argcheck(L, l || lua_isnoneornil(L, 1), 1, "string expected"); + luaL_argcheck(L, op != -1, 2, "invalid option"); + lua_pushstring(L, setlocale(cat[op], l)); + return 1; +} + + +static int io_exit (lua_State *L) { + exit(luaL_optint(L, 1, EXIT_SUCCESS)); + return 0; /* to avoid warnings */ +} + +static const luaL_reg syslib[] = { + {"clock", io_clock}, + {"date", io_date}, + {"difftime", io_difftime}, + {"execute", io_execute}, + {"exit", io_exit}, + {"getenv", io_getenv}, + {"remove", io_remove}, + {"rename", io_rename}, + {"setlocale", io_setloc}, + {"time", io_time}, + {"tmpname", io_tmpname}, + {NULL, NULL} +}; + +/* }====================================================== */ + + + +LUALIB_API int luaopen_io (lua_State *L) { + luaL_openlib(L, LUA_OSLIBNAME, syslib, 0); + createmeta(L); + lua_pushvalue(L, -1); + luaL_openlib(L, LUA_IOLIBNAME, iolib, 1); + /* put predefined file handles into `io' table */ + registerfile(L, stdin, "stdin", IO_INPUT); + registerfile(L, stdout, "stdout", IO_OUTPUT); + registerfile(L, stderr, "stderr", NULL); + return 1; +} + diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lmathlib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lmathlib.c index 93eb3cba9c..266cc707f7 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lmathlib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lmathlib.c @@ -1,256 +1,256 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include -#include - -#define lmathlib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - -#undef PI -#define PI (3.14159265358979323846) -#define RADIANS_PER_DEGREE (PI/180.0) - - - -/* -** If you want Lua to operate in degrees (instead of radians), -** define USE_DEGREES -*/ -#ifdef USE_DEGREES -#define FROMRAD(a) ((a)/RADIANS_PER_DEGREE) -#define TORAD(a) ((a)*RADIANS_PER_DEGREE) -#else -#define FROMRAD(a) (a) -#define TORAD(a) (a) -#endif - - -static int math_abs (lua_State *L) { - lua_pushnumber(L, fabs(luaL_checknumber(L, 1))); - return 1; -} - -static int math_sin (lua_State *L) { - lua_pushnumber(L, sin(TORAD(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_cos (lua_State *L) { - lua_pushnumber(L, cos(TORAD(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_tan (lua_State *L) { - lua_pushnumber(L, tan(TORAD(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_asin (lua_State *L) { - lua_pushnumber(L, FROMRAD(asin(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_acos (lua_State *L) { - lua_pushnumber(L, FROMRAD(acos(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_atan (lua_State *L) { - lua_pushnumber(L, FROMRAD(atan(luaL_checknumber(L, 1)))); - return 1; -} - -static int math_atan2 (lua_State *L) { - lua_pushnumber(L, FROMRAD(atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2)))); - return 1; -} - -static int math_ceil (lua_State *L) { - lua_pushnumber(L, ceil(luaL_checknumber(L, 1))); - return 1; -} - -static int math_floor (lua_State *L) { - lua_pushnumber(L, floor(luaL_checknumber(L, 1))); - return 1; -} - -static int math_mod (lua_State *L) { - lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); - return 1; -} - -static int math_sqrt (lua_State *L) { - lua_pushnumber(L, sqrt(luaL_checknumber(L, 1))); - return 1; -} - -static int math_pow (lua_State *L) { - lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); - return 1; -} - -static int math_log (lua_State *L) { - lua_pushnumber(L, log(luaL_checknumber(L, 1))); - return 1; -} - -static int math_log10 (lua_State *L) { - lua_pushnumber(L, log10(luaL_checknumber(L, 1))); - return 1; -} - -static int math_exp (lua_State *L) { - lua_pushnumber(L, exp(luaL_checknumber(L, 1))); - return 1; -} - -static int math_deg (lua_State *L) { - lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE); - return 1; -} - -static int math_rad (lua_State *L) { - lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE); - return 1; -} - -static int math_frexp (lua_State *L) { - int e; - lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e)); - lua_pushnumber(L, e); - return 2; -} - -static int math_ldexp (lua_State *L) { - lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2))); - return 1; -} - - - -static int math_min (lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - lua_Number dmin = luaL_checknumber(L, 1); - int i; - for (i=2; i<=n; i++) { - lua_Number d = luaL_checknumber(L, i); - if (d < dmin) - dmin = d; - } - lua_pushnumber(L, dmin); - return 1; -} - - -static int math_max (lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - lua_Number dmax = luaL_checknumber(L, 1); - int i; - for (i=2; i<=n; i++) { - lua_Number d = luaL_checknumber(L, i); - if (d > dmax) - dmax = d; - } - lua_pushnumber(L, dmax); - return 1; -} - - -static int math_random (lua_State *L) { - /* the `%' avoids the (rare) case of r==1, and is needed also because on - some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ - lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX; - switch (lua_gettop(L)) { /* check number of arguments */ - case 0: { /* no arguments */ - lua_pushnumber(L, r); /* Number between 0 and 1 */ - break; - } - case 1: { /* only upper limit */ - int u = luaL_checkint(L, 1); - luaL_argcheck(L, 1<=u, 1, "interval is empty"); - lua_pushnumber(L, (int)floor(r*u)+1); /* int between 1 and `u' */ - break; - } - case 2: { /* lower and upper limits */ - int l = luaL_checkint(L, 1); - int u = luaL_checkint(L, 2); - luaL_argcheck(L, l<=u, 2, "interval is empty"); - lua_pushnumber(L, (int)floor(r*(u-l+1))+l); /* int between `l' and `u' */ - break; - } - default: return luaL_error(L, "wrong number of arguments"); - } - return 1; -} - - -static int math_randomseed (lua_State *L) { - srand(luaL_checkint(L, 1)); - return 0; -} - - -static const luaL_reg mathlib[] = { - {"abs", math_abs}, - {"sin", math_sin}, - {"cos", math_cos}, - {"tan", math_tan}, - {"asin", math_asin}, - {"acos", math_acos}, - {"atan", math_atan}, - {"atan2", math_atan2}, - {"ceil", math_ceil}, - {"floor", math_floor}, - {"mod", math_mod}, - {"frexp", math_frexp}, - {"ldexp", math_ldexp}, - {"sqrt", math_sqrt}, - {"min", math_min}, - {"max", math_max}, - {"log", math_log}, - {"log10", math_log10}, - {"exp", math_exp}, - {"deg", math_deg}, - {"pow", math_pow}, - {"rad", math_rad}, - {"random", math_random}, - {"randomseed", math_randomseed}, - {NULL, NULL} -}; - - -/* -** Open math library -*/ -LUALIB_API int luaopen_math (lua_State *L) { - luaL_openlib(L, LUA_MATHLIBNAME, mathlib, 0); - lua_pushliteral(L, "pi"); - lua_pushnumber(L, PI); - lua_settable(L, -3); - lua_pushliteral(L, "__pow"); - lua_pushcfunction(L, math_pow); - lua_settable(L, LUA_GLOBALSINDEX); - return 1; -} - +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include +#include + +#define lmathlib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#undef PI +#define PI (3.14159265358979323846) +#define RADIANS_PER_DEGREE (PI/180.0) + + + +/* +** If you want Lua to operate in degrees (instead of radians), +** define USE_DEGREES +*/ +#ifdef USE_DEGREES +#define FROMRAD(a) ((a)/RADIANS_PER_DEGREE) +#define TORAD(a) ((a)*RADIANS_PER_DEGREE) +#else +#define FROMRAD(a) (a) +#define TORAD(a) (a) +#endif + + +static int math_abs (lua_State *L) { + lua_pushnumber(L, fabs(luaL_checknumber(L, 1))); + return 1; +} + +static int math_sin (lua_State *L) { + lua_pushnumber(L, sin(TORAD(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_cos (lua_State *L) { + lua_pushnumber(L, cos(TORAD(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_tan (lua_State *L) { + lua_pushnumber(L, tan(TORAD(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_asin (lua_State *L) { + lua_pushnumber(L, FROMRAD(asin(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_acos (lua_State *L) { + lua_pushnumber(L, FROMRAD(acos(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_atan (lua_State *L) { + lua_pushnumber(L, FROMRAD(atan(luaL_checknumber(L, 1)))); + return 1; +} + +static int math_atan2 (lua_State *L) { + lua_pushnumber(L, FROMRAD(atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2)))); + return 1; +} + +static int math_ceil (lua_State *L) { + lua_pushnumber(L, ceil(luaL_checknumber(L, 1))); + return 1; +} + +static int math_floor (lua_State *L) { + lua_pushnumber(L, floor(luaL_checknumber(L, 1))); + return 1; +} + +static int math_mod (lua_State *L) { + lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int math_sqrt (lua_State *L) { + lua_pushnumber(L, sqrt(luaL_checknumber(L, 1))); + return 1; +} + +static int math_pow (lua_State *L) { + lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int math_log (lua_State *L) { + lua_pushnumber(L, log(luaL_checknumber(L, 1))); + return 1; +} + +static int math_log10 (lua_State *L) { + lua_pushnumber(L, log10(luaL_checknumber(L, 1))); + return 1; +} + +static int math_exp (lua_State *L) { + lua_pushnumber(L, exp(luaL_checknumber(L, 1))); + return 1; +} + +static int math_deg (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE); + return 1; +} + +static int math_rad (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE); + return 1; +} + +static int math_frexp (lua_State *L) { + int e; + lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e)); + lua_pushnumber(L, e); + return 2; +} + +static int math_ldexp (lua_State *L) { + lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2))); + return 1; +} + + + +static int math_min (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + lua_Number dmin = luaL_checknumber(L, 1); + int i; + for (i=2; i<=n; i++) { + lua_Number d = luaL_checknumber(L, i); + if (d < dmin) + dmin = d; + } + lua_pushnumber(L, dmin); + return 1; +} + + +static int math_max (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + lua_Number dmax = luaL_checknumber(L, 1); + int i; + for (i=2; i<=n; i++) { + lua_Number d = luaL_checknumber(L, i); + if (d > dmax) + dmax = d; + } + lua_pushnumber(L, dmax); + return 1; +} + + +static int math_random (lua_State *L) { + /* the `%' avoids the (rare) case of r==1, and is needed also because on + some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ + lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX; + switch (lua_gettop(L)) { /* check number of arguments */ + case 0: { /* no arguments */ + lua_pushnumber(L, r); /* Number between 0 and 1 */ + break; + } + case 1: { /* only upper limit */ + int u = luaL_checkint(L, 1); + luaL_argcheck(L, 1<=u, 1, "interval is empty"); + lua_pushnumber(L, (int)floor(r*u)+1); /* int between 1 and `u' */ + break; + } + case 2: { /* lower and upper limits */ + int l = luaL_checkint(L, 1); + int u = luaL_checkint(L, 2); + luaL_argcheck(L, l<=u, 2, "interval is empty"); + lua_pushnumber(L, (int)floor(r*(u-l+1))+l); /* int between `l' and `u' */ + break; + } + default: return luaL_error(L, "wrong number of arguments"); + } + return 1; +} + + +static int math_randomseed (lua_State *L) { + srand(luaL_checkint(L, 1)); + return 0; +} + + +static const luaL_reg mathlib[] = { + {"abs", math_abs}, + {"sin", math_sin}, + {"cos", math_cos}, + {"tan", math_tan}, + {"asin", math_asin}, + {"acos", math_acos}, + {"atan", math_atan}, + {"atan2", math_atan2}, + {"ceil", math_ceil}, + {"floor", math_floor}, + {"mod", math_mod}, + {"frexp", math_frexp}, + {"ldexp", math_ldexp}, + {"sqrt", math_sqrt}, + {"min", math_min}, + {"max", math_max}, + {"log", math_log}, + {"log10", math_log10}, + {"exp", math_exp}, + {"deg", math_deg}, + {"pow", math_pow}, + {"rad", math_rad}, + {"random", math_random}, + {"randomseed", math_randomseed}, + {NULL, NULL} +}; + + +/* +** Open math library +*/ +LUALIB_API int luaopen_math (lua_State *L) { + luaL_openlib(L, LUA_MATHLIBNAME, mathlib, 0); + lua_pushliteral(L, "pi"); + lua_pushnumber(L, PI); + lua_settable(L, -3); + lua_pushliteral(L, "__pow"); + lua_pushcfunction(L, math_pow); + lua_settable(L, LUA_GLOBALSINDEX); + return 1; +} + diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/loadlib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/loadlib.c index 6f4b8bb42d..e28d6018fb 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/loadlib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/loadlib.c @@ -1,205 +1,205 @@ -/* -** $Id: loadlib.c,v 1.1 2005/10/25 09:41:32 vizerie Exp $ -** Dynamic library loader for Lua -** See Copyright Notice in lua.h -* -* This Lua library exports a single function, called loadlib, which is -* called from Lua as loadlib(lib,init), where lib is the full name of the -* library to be loaded (including the complete path) and init is the name -* of a function to be called after the library is loaded. Typically, this -* function will register other functions, thus making the complete library -* available to Lua. The init function is *not* automatically called by -* loadlib. Instead, loadlib returns the init function as a Lua function -* that the client can call when it thinks is appropriate. In the case of -* errors, loadlib returns nil and two strings describing the error. -* The first string is supplied by the operating system; it should be -* informative and useful for error messages. The second string is "open", -* "init", or "absent" to identify the error and is meant to be used for -* making decisions without having to look into the first string (whose -* format is system-dependent). -* -* This module contains an implementation of loadlib for Unix systems that -* have dlfcn, an implementation for Windows, and a stub for other systems. -* See the list at the end of this file for some links to available -* implementations of dlfcn and interfaces to other native dynamic loaders -* on top of which loadlib could be implemented. -* -*/ - -#include "lua.h" -#include "lauxlib.h" -#include "lualib.h" - - -#undef LOADLIB - - -#ifdef USE_DLOPEN -#define LOADLIB -/* -* This is an implementation of loadlib based on the dlfcn interface. -* The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, -* NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least -* as an emulation layer on top of native functions. -*/ - -#include - -static int loadlib(lua_State *L) -{ - const char *path=luaL_checkstring(L,1); - const char *init=luaL_checkstring(L,2); - void *lib=dlopen(path,RTLD_NOW); - if (lib!=NULL) - { - lua_CFunction f=(lua_CFunction) dlsym(lib,init); - if (f!=NULL) - { - lua_pushlightuserdata(L,lib); - lua_pushcclosure(L,f,1); - return 1; - } - } - /* else return appropriate error messages */ - lua_pushnil(L); - lua_pushstring(L,dlerror()); - lua_pushstring(L,(lib!=NULL) ? "init" : "open"); - if (lib!=NULL) dlclose(lib); - return 3; -} - -#endif - - - -/* -** In Windows, default is to use dll; otherwise, default is not to use dll -*/ -#ifndef USE_DLL -#ifdef _WIN32 -#define USE_DLL 1 -#else -#define USE_DLL 0 -#endif -#endif - - -#if USE_DLL -#define LOADLIB -/* -* This is an implementation of loadlib for Windows using native functions. -*/ - -#include - -static void pusherror(lua_State *L) -{ - int error=GetLastError(); - char buffer[128]; - if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, - 0, error, 0, buffer, sizeof(buffer), 0)) - lua_pushstring(L,buffer); - else - lua_pushfstring(L,"system error %d\n",error); -} - -static int loadlib(lua_State *L) -{ - const char *path=luaL_checkstring(L,1); - const char *init=luaL_checkstring(L,2); - HINSTANCE lib=LoadLibrary(path); - if (lib!=NULL) - { - lua_CFunction f=(lua_CFunction) GetProcAddress(lib,init); - if (f!=NULL) - { - lua_pushlightuserdata(L,lib); - lua_pushcclosure(L,f,1); - return 1; - } - } - lua_pushnil(L); - pusherror(L); - lua_pushstring(L,(lib!=NULL) ? "init" : "open"); - if (lib!=NULL) FreeLibrary(lib); - return 3; -} - -#endif - - - -#ifndef LOADLIB -/* Fallback for other systems */ - -/* -** Those systems support dlopen, so they should have defined USE_DLOPEN. -** The default (no)implementation gives them a special error message. -*/ -#ifdef linux -#define LOADLIB -#endif - -#ifdef sun -#define LOADLIB -#endif - -#ifdef sgi -#define LOADLIB -#endif - -#ifdef BSD -#define LOADLIB -#endif - -#ifdef _WIN32 -#define LOADLIB -#endif - -#ifdef LOADLIB -#undef LOADLIB -#define LOADLIB "`loadlib' not installed (check your Lua configuration)" -#else -#define LOADLIB "`loadlib' not supported" -#endif - -static int loadlib(lua_State *L) -{ - lua_pushnil(L); - lua_pushliteral(L,LOADLIB); - lua_pushliteral(L,"absent"); - return 3; -} -#endif - -LUALIB_API int luaopen_loadlib (lua_State *L) -{ - lua_register(L,"loadlib",loadlib); - return 0; -} - -/* -* Here are some links to available implementations of dlfcn and -* interfaces to other native dynamic loaders on top of which loadlib -* could be implemented. Please send contributions and corrections to us. -* -* AIX -* Starting with AIX 4.2, dlfcn is included in the base OS. -* There is also an emulation package available. -* http://www.faqs.org/faqs/aix-faq/part4/section-21.html -* -* HPUX -* HPUX 11 has dlfcn. For HPUX 10 use shl_*. -* http://www.geda.seul.org/mailinglist/geda-dev37/msg00094.html -* http://www.stat.umn.edu/~luke/xls/projects/dlbasics/dlbasics.html -* -* Macintosh, Windows -* http://www.stat.umn.edu/~luke/xls/projects/dlbasics/dlbasics.html -* -* Mac OS X/Darwin -* http://www.opendarwin.org/projects/dlcompat/ -* -* GLIB has wrapper code for BeOS, OS2, Unix and Windows -* http://cvs.gnome.org/lxr/source/glib/gmodule/ -* -*/ +/* +** $Id: loadlib.c,v 1.1 2005/10/25 09:41:32 vizerie Exp $ +** Dynamic library loader for Lua +** See Copyright Notice in lua.h +* +* This Lua library exports a single function, called loadlib, which is +* called from Lua as loadlib(lib,init), where lib is the full name of the +* library to be loaded (including the complete path) and init is the name +* of a function to be called after the library is loaded. Typically, this +* function will register other functions, thus making the complete library +* available to Lua. The init function is *not* automatically called by +* loadlib. Instead, loadlib returns the init function as a Lua function +* that the client can call when it thinks is appropriate. In the case of +* errors, loadlib returns nil and two strings describing the error. +* The first string is supplied by the operating system; it should be +* informative and useful for error messages. The second string is "open", +* "init", or "absent" to identify the error and is meant to be used for +* making decisions without having to look into the first string (whose +* format is system-dependent). +* +* This module contains an implementation of loadlib for Unix systems that +* have dlfcn, an implementation for Windows, and a stub for other systems. +* See the list at the end of this file for some links to available +* implementations of dlfcn and interfaces to other native dynamic loaders +* on top of which loadlib could be implemented. +* +*/ + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + + +#undef LOADLIB + + +#ifdef USE_DLOPEN +#define LOADLIB +/* +* This is an implementation of loadlib based on the dlfcn interface. +* The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, +* NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least +* as an emulation layer on top of native functions. +*/ + +#include + +static int loadlib(lua_State *L) +{ + const char *path=luaL_checkstring(L,1); + const char *init=luaL_checkstring(L,2); + void *lib=dlopen(path,RTLD_NOW); + if (lib!=NULL) + { + lua_CFunction f=(lua_CFunction) dlsym(lib,init); + if (f!=NULL) + { + lua_pushlightuserdata(L,lib); + lua_pushcclosure(L,f,1); + return 1; + } + } + /* else return appropriate error messages */ + lua_pushnil(L); + lua_pushstring(L,dlerror()); + lua_pushstring(L,(lib!=NULL) ? "init" : "open"); + if (lib!=NULL) dlclose(lib); + return 3; +} + +#endif + + + +/* +** In Windows, default is to use dll; otherwise, default is not to use dll +*/ +#ifndef USE_DLL +#ifdef _WIN32 +#define USE_DLL 1 +#else +#define USE_DLL 0 +#endif +#endif + + +#if USE_DLL +#define LOADLIB +/* +* This is an implementation of loadlib for Windows using native functions. +*/ + +#include + +static void pusherror(lua_State *L) +{ + int error=GetLastError(); + char buffer[128]; + if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, + 0, error, 0, buffer, sizeof(buffer), 0)) + lua_pushstring(L,buffer); + else + lua_pushfstring(L,"system error %d\n",error); +} + +static int loadlib(lua_State *L) +{ + const char *path=luaL_checkstring(L,1); + const char *init=luaL_checkstring(L,2); + HINSTANCE lib=LoadLibrary(path); + if (lib!=NULL) + { + lua_CFunction f=(lua_CFunction) GetProcAddress(lib,init); + if (f!=NULL) + { + lua_pushlightuserdata(L,lib); + lua_pushcclosure(L,f,1); + return 1; + } + } + lua_pushnil(L); + pusherror(L); + lua_pushstring(L,(lib!=NULL) ? "init" : "open"); + if (lib!=NULL) FreeLibrary(lib); + return 3; +} + +#endif + + + +#ifndef LOADLIB +/* Fallback for other systems */ + +/* +** Those systems support dlopen, so they should have defined USE_DLOPEN. +** The default (no)implementation gives them a special error message. +*/ +#ifdef linux +#define LOADLIB +#endif + +#ifdef sun +#define LOADLIB +#endif + +#ifdef sgi +#define LOADLIB +#endif + +#ifdef BSD +#define LOADLIB +#endif + +#ifdef _WIN32 +#define LOADLIB +#endif + +#ifdef LOADLIB +#undef LOADLIB +#define LOADLIB "`loadlib' not installed (check your Lua configuration)" +#else +#define LOADLIB "`loadlib' not supported" +#endif + +static int loadlib(lua_State *L) +{ + lua_pushnil(L); + lua_pushliteral(L,LOADLIB); + lua_pushliteral(L,"absent"); + return 3; +} +#endif + +LUALIB_API int luaopen_loadlib (lua_State *L) +{ + lua_register(L,"loadlib",loadlib); + return 0; +} + +/* +* Here are some links to available implementations of dlfcn and +* interfaces to other native dynamic loaders on top of which loadlib +* could be implemented. Please send contributions and corrections to us. +* +* AIX +* Starting with AIX 4.2, dlfcn is included in the base OS. +* There is also an emulation package available. +* http://www.faqs.org/faqs/aix-faq/part4/section-21.html +* +* HPUX +* HPUX 11 has dlfcn. For HPUX 10 use shl_*. +* http://www.geda.seul.org/mailinglist/geda-dev37/msg00094.html +* http://www.stat.umn.edu/~luke/xls/projects/dlbasics/dlbasics.html +* +* Macintosh, Windows +* http://www.stat.umn.edu/~luke/xls/projects/dlbasics/dlbasics.html +* +* Mac OS X/Darwin +* http://www.opendarwin.org/projects/dlcompat/ +* +* GLIB has wrapper code for BeOS, OS2, Unix and Windows +* http://cvs.gnome.org/lxr/source/glib/gmodule/ +* +*/ diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lstrlib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lstrlib.c index 69e40064b9..28cbe0bf42 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lstrlib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/lstrlib.c @@ -1,780 +1,780 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include -#include -#include -#include -#include - -#define lstrlib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - -/* macro to `unsign' a character */ -#ifndef uchar -#define uchar(c) ((unsigned char)(c)) -#endif - - -typedef long sint32; /* a signed version for size_t */ - - -static int str_len (lua_State *L) { - size_t l; - luaL_checklstring(L, 1, &l); - lua_pushnumber(L, (lua_Number)l); - return 1; -} - - -static sint32 posrelat (sint32 pos, size_t len) { - /* relative string position: negative means back from end */ - return (pos>=0) ? pos : (sint32)len+pos+1; -} - - -static int str_sub (lua_State *L) { - size_t l; - const char *s = luaL_checklstring(L, 1, &l); - sint32 start = posrelat(luaL_checklong(L, 2), l); - sint32 end = posrelat(luaL_optlong(L, 3, -1), l); - if (start < 1) start = 1; - if (end > (sint32)l) end = (sint32)l; - if (start <= end) - lua_pushlstring(L, s+start-1, end-start+1); - else lua_pushliteral(L, ""); - return 1; -} - - -static int str_lower (lua_State *L) { - size_t l; - size_t i; - luaL_Buffer b; - const char *s = luaL_checklstring(L, 1, &l); - luaL_buffinit(L, &b); - for (i=0; i 0) - luaL_addlstring(&b, s, l); - luaL_pushresult(&b); - return 1; -} - - -static int str_byte (lua_State *L) { - size_t l; - const char *s = luaL_checklstring(L, 1, &l); - sint32 pos = posrelat(luaL_optlong(L, 2, 1), l); - if (pos <= 0 || (size_t)(pos) > l) /* index out of range? */ - return 0; /* no answer */ - lua_pushnumber(L, uchar(s[pos-1])); - return 1; -} - - -static int str_char (lua_State *L) { - int n = lua_gettop(L); /* number of arguments */ - int i; - luaL_Buffer b; - luaL_buffinit(L, &b); - for (i=1; i<=n; i++) { - int c = luaL_checkint(L, i); - luaL_argcheck(L, uchar(c) == c, i, "invalid value"); - luaL_putchar(&b, uchar(c)); - } - luaL_pushresult(&b); - return 1; -} - - -static int writer (lua_State *L, const void* b, size_t size, void* B) { - (void)L; - luaL_addlstring((luaL_Buffer*) B, (const char *)b, size); - return 1; -} - - -static int str_dump (lua_State *L) { - luaL_Buffer b; - luaL_checktype(L, 1, LUA_TFUNCTION); - luaL_buffinit(L,&b); - if (!lua_dump(L, writer, &b)) - luaL_error(L, "unable to dump given function"); - luaL_pushresult(&b); - return 1; -} - - - -/* -** {====================================================== -** PATTERN MATCHING -** ======================================================= -*/ - -#ifndef MAX_CAPTURES -#define MAX_CAPTURES 32 /* arbitrary limit */ -#endif - - -#define CAP_UNFINISHED (-1) -#define CAP_POSITION (-2) - -typedef struct MatchState { - const char *src_init; /* init of source string */ - const char *src_end; /* end (`\0') of source string */ - lua_State *L; - int level; /* total number of captures (finished or unfinished) */ - struct { - const char *init; - sint32 len; - } capture[MAX_CAPTURES]; -} MatchState; - - -#define ESC '%' -#define SPECIALS "^$*+?.([%-" - - -static int check_capture (MatchState *ms, int l) { - l -= '1'; - if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) - return luaL_error(ms->L, "invalid capture index"); - return l; -} - - -static int capture_to_close (MatchState *ms) { - int level = ms->level; - for (level--; level>=0; level--) - if (ms->capture[level].len == CAP_UNFINISHED) return level; - return luaL_error(ms->L, "invalid pattern capture"); -} - - -static const char *luaI_classend (MatchState *ms, const char *p) { - switch (*p++) { - case ESC: { - if (*p == '\0') - luaL_error(ms->L, "malformed pattern (ends with `%')"); - return p+1; - } - case '[': { - if (*p == '^') p++; - do { /* look for a `]' */ - if (*p == '\0') - luaL_error(ms->L, "malformed pattern (missing `]')"); - if (*(p++) == ESC && *p != '\0') - p++; /* skip escapes (e.g. `%]') */ - } while (*p != ']'); - return p+1; - } - default: { - return p; - } - } -} - - -static int match_class (int c, int cl) { - int res; - switch (tolower(cl)) { - case 'a' : res = isalpha(c); break; - case 'c' : res = iscntrl(c); break; - case 'd' : res = isdigit(c); break; - case 'l' : res = islower(c); break; - case 'p' : res = ispunct(c); break; - case 's' : res = isspace(c); break; - case 'u' : res = isupper(c); break; - case 'w' : res = isalnum(c); break; - case 'x' : res = isxdigit(c); break; - case 'z' : res = (c == 0); break; - default: return (cl == c); - } - return (islower(cl) ? res : !res); -} - - -static int matchbracketclass (int c, const char *p, const char *ec) { - int sig = 1; - if (*(p+1) == '^') { - sig = 0; - p++; /* skip the `^' */ - } - while (++p < ec) { - if (*p == ESC) { - p++; - if (match_class(c, *p)) - return sig; - } - else if ((*(p+1) == '-') && (p+2 < ec)) { - p+=2; - if (uchar(*(p-2)) <= c && c <= uchar(*p)) - return sig; - } - else if (uchar(*p) == c) return sig; - } - return !sig; -} - - -static int luaI_singlematch (int c, const char *p, const char *ep) { - switch (*p) { - case '.': return 1; /* matches any char */ - case ESC: return match_class(c, *(p+1)); - case '[': return matchbracketclass(c, p, ep-1); - default: return (uchar(*p) == c); - } -} - - -static const char *match (MatchState *ms, const char *s, const char *p); - - -static const char *matchbalance (MatchState *ms, const char *s, - const char *p) { - if (*p == 0 || *(p+1) == 0) - luaL_error(ms->L, "unbalanced pattern"); - if (*s != *p) return NULL; - else { - int b = *p; - int e = *(p+1); - int cont = 1; - while (++s < ms->src_end) { - if (*s == e) { - if (--cont == 0) return s+1; - } - else if (*s == b) cont++; - } - } - return NULL; /* string ends out of balance */ -} - - -static const char *max_expand (MatchState *ms, const char *s, - const char *p, const char *ep) { - sint32 i = 0; /* counts maximum expand for item */ - while ((s+i)src_end && luaI_singlematch(uchar(*(s+i)), p, ep)) - i++; - /* keeps trying to match with the maximum repetitions */ - while (i>=0) { - const char *res = match(ms, (s+i), ep+1); - if (res) return res; - i--; /* else didn't match; reduce 1 repetition to try again */ - } - return NULL; -} - - -static const char *min_expand (MatchState *ms, const char *s, - const char *p, const char *ep) { - for (;;) { - const char *res = match(ms, s, ep+1); - if (res != NULL) - return res; - else if (ssrc_end && luaI_singlematch(uchar(*s), p, ep)) - s++; /* try with one more repetition */ - else return NULL; - } -} - - -static const char *start_capture (MatchState *ms, const char *s, - const char *p, int what) { - const char *res; - int level = ms->level; - if (level >= MAX_CAPTURES) luaL_error(ms->L, "too many captures"); - ms->capture[level].init = s; - ms->capture[level].len = what; - ms->level = level+1; - if ((res=match(ms, s, p)) == NULL) /* match failed? */ - ms->level--; /* undo capture */ - return res; -} - - -static const char *end_capture (MatchState *ms, const char *s, - const char *p) { - int l = capture_to_close(ms); - const char *res; - ms->capture[l].len = s - ms->capture[l].init; /* close capture */ - if ((res = match(ms, s, p)) == NULL) /* match failed? */ - ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ - return res; -} - - -static const char *match_capture (MatchState *ms, const char *s, int l) { - size_t len; - l = check_capture(ms, l); - len = ms->capture[l].len; - if ((size_t)(ms->src_end-s) >= len && - memcmp(ms->capture[l].init, s, len) == 0) - return s+len; - else return NULL; -} - - -static const char *match (MatchState *ms, const char *s, const char *p) { - init: /* using goto's to optimize tail recursion */ - switch (*p) { - case '(': { /* start capture */ - if (*(p+1) == ')') /* position capture? */ - return start_capture(ms, s, p+2, CAP_POSITION); - else - return start_capture(ms, s, p+1, CAP_UNFINISHED); - } - case ')': { /* end capture */ - return end_capture(ms, s, p+1); - } - case ESC: { - switch (*(p+1)) { - case 'b': { /* balanced string? */ - s = matchbalance(ms, s, p+2); - if (s == NULL) return NULL; - p+=4; goto init; /* else return match(ms, s, p+4); */ - } - case 'f': { /* frontier? */ - const char *ep; char previous; - p += 2; - if (*p != '[') - luaL_error(ms->L, "missing `[' after `%%f' in pattern"); - ep = luaI_classend(ms, p); /* points to what is next */ - previous = (s == ms->src_init) ? '\0' : *(s-1); - if (matchbracketclass(uchar(previous), p, ep-1) || - !matchbracketclass(uchar(*s), p, ep-1)) return NULL; - p=ep; goto init; /* else return match(ms, s, ep); */ - } - default: { - if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */ - s = match_capture(ms, s, *(p+1)); - if (s == NULL) return NULL; - p+=2; goto init; /* else return match(ms, s, p+2) */ - } - goto dflt; /* case default */ - } - } - } - case '\0': { /* end of pattern */ - return s; /* match succeeded */ - } - case '$': { - if (*(p+1) == '\0') /* is the `$' the last char in pattern? */ - return (s == ms->src_end) ? s : NULL; /* check end of string */ - else goto dflt; - } - default: dflt: { /* it is a pattern item */ - const char *ep = luaI_classend(ms, p); /* points to what is next */ - int m = ssrc_end && luaI_singlematch(uchar(*s), p, ep); - switch (*ep) { - case '?': { /* optional */ - const char *res; - if (m && ((res=match(ms, s+1, ep+1)) != NULL)) - return res; - p=ep+1; goto init; /* else return match(ms, s, ep+1); */ - } - case '*': { /* 0 or more repetitions */ - return max_expand(ms, s, p, ep); - } - case '+': { /* 1 or more repetitions */ - return (m ? max_expand(ms, s+1, p, ep) : NULL); - } - case '-': { /* 0 or more repetitions (minimum) */ - return min_expand(ms, s, p, ep); - } - default: { - if (!m) return NULL; - s++; p=ep; goto init; /* else return match(ms, s+1, ep); */ - } - } - } - } -} - - - -static const char *lmemfind (const char *s1, size_t l1, - const char *s2, size_t l2) { - if (l2 == 0) return s1; /* empty strings are everywhere */ - else if (l2 > l1) return NULL; /* avoids a negative `l1' */ - else { - const char *init; /* to search for a `*s2' inside `s1' */ - l2--; /* 1st char will be checked by `memchr' */ - l1 = l1-l2; /* `s2' cannot be found after that */ - while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { - init++; /* 1st char is already checked */ - if (memcmp(init, s2+1, l2) == 0) - return init-1; - else { /* correct `l1' and `s1' to try again */ - l1 -= init-s1; - s1 = init; - } - } - return NULL; /* not found */ - } -} - - -static void push_onecapture (MatchState *ms, int i) { - int l = ms->capture[i].len; - if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); - if (l == CAP_POSITION) - lua_pushnumber(ms->L, (lua_Number)(ms->capture[i].init - ms->src_init + 1)); - else - lua_pushlstring(ms->L, ms->capture[i].init, l); -} - - -static int push_captures (MatchState *ms, const char *s, const char *e) { - int i; - luaL_checkstack(ms->L, ms->level, "too many captures"); - if (ms->level == 0 && s) { /* no explicit captures? */ - lua_pushlstring(ms->L, s, e-s); /* return whole match */ - return 1; - } - else { /* return all captures */ - for (i=0; ilevel; i++) - push_onecapture(ms, i); - return ms->level; /* number of strings pushed */ - } -} - - -static int str_find (lua_State *L) { - size_t l1, l2; - const char *s = luaL_checklstring(L, 1, &l1); - const char *p = luaL_checklstring(L, 2, &l2); - sint32 init = posrelat(luaL_optlong(L, 3, 1), l1) - 1; - if (init < 0) init = 0; - else if ((size_t)(init) > l1) init = (sint32)l1; - if (lua_toboolean(L, 4) || /* explicit request? */ - strpbrk(p, SPECIALS) == NULL) { /* or no special characters? */ - /* do a plain search */ - const char *s2 = lmemfind(s+init, l1-init, p, l2); - if (s2) { - lua_pushnumber(L, (lua_Number)(s2-s+1)); - lua_pushnumber(L, (lua_Number)(s2-s+l2)); - return 2; - } - } - else { - MatchState ms; - int anchor = (*p == '^') ? (p++, 1) : 0; - const char *s1=s+init; - ms.L = L; - ms.src_init = s; - ms.src_end = s+l1; - do { - const char *res; - ms.level = 0; - if ((res=match(&ms, s1, p)) != NULL) { - lua_pushnumber(L, (lua_Number)(s1-s+1)); /* start */ - lua_pushnumber(L, (lua_Number)(res-s)); /* end */ - return push_captures(&ms, NULL, 0) + 2; - } - } while (s1++L; - if (lua_isstring(L, 3)) { - const char *news = lua_tostring(L, 3); - size_t l = lua_strlen(L, 3); - size_t i; - for (i=0; i= 3 && (lua_isstring(L, 3) || lua_isfunction(L, 3)), - 3, "string or function expected"); - luaL_buffinit(L, &b); - ms.L = L; - ms.src_init = src; - ms.src_end = src+srcl; - while (n < max_s) { - const char *e; - ms.level = 0; - e = match(&ms, src, p); - if (e) { - n++; - add_s(&ms, &b, src, e); - } - if (e && e>src) /* non empty match? */ - src = e; /* skip it */ - else if (src < ms.src_end) - luaL_putchar(&b, *src++); - else break; - if (anchor) break; - } - luaL_addlstring(&b, src, ms.src_end-src); - luaL_pushresult(&b); - lua_pushnumber(L, (lua_Number)n); /* number of substitutions */ - return 2; -} - -/* }====================================================== */ - - -/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ -#define MAX_ITEM 512 -/* maximum size of each format specification (such as '%-099.99d') */ -#define MAX_FORMAT 20 - - -static void luaI_addquoted (lua_State *L, luaL_Buffer *b, int arg) { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); - luaL_putchar(b, '"'); - while (l--) { - switch (*s) { - case '"': case '\\': case '\n': { - luaL_putchar(b, '\\'); - luaL_putchar(b, *s); - break; - } - case '\0': { - luaL_addlstring(b, "\\000", 4); - break; - } - default: { - luaL_putchar(b, *s); - break; - } - } - s++; - } - luaL_putchar(b, '"'); -} - - -static const char *scanformat (lua_State *L, const char *strfrmt, - char *form, int *hasprecision) { - const char *p = strfrmt; - while (strchr("-+ #0", *p)) p++; /* skip flags */ - if (isdigit(uchar(*p))) p++; /* skip width */ - if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ - if (*p == '.') { - p++; - *hasprecision = 1; - if (isdigit(uchar(*p))) p++; /* skip precision */ - if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ - } - if (isdigit(uchar(*p))) - luaL_error(L, "invalid format (width or precision too long)"); - if (p-strfrmt+2 > MAX_FORMAT) /* +2 to include `%' and the specifier */ - luaL_error(L, "invalid format (too long)"); - form[0] = '%'; - strncpy(form+1, strfrmt, p-strfrmt+1); - form[p-strfrmt+2] = 0; - return p; -} - - -static int str_format (lua_State *L) { - int arg = 1; - size_t sfl; - const char *strfrmt = luaL_checklstring(L, arg, &sfl); - const char *strfrmt_end = strfrmt+sfl; - luaL_Buffer b; - luaL_buffinit(L, &b); - while (strfrmt < strfrmt_end) { - if (*strfrmt != '%') - luaL_putchar(&b, *strfrmt++); - else if (*++strfrmt == '%') - luaL_putchar(&b, *strfrmt++); /* %% */ - else { /* format item */ - char form[MAX_FORMAT]; /* to store the format (`%...') */ - char buff[MAX_ITEM]; /* to store the formatted item */ - int hasprecision = 0; - if (isdigit(uchar(*strfrmt)) && *(strfrmt+1) == '$') - return luaL_error(L, "obsolete option (d$) to `format'"); - arg++; - strfrmt = scanformat(L, strfrmt, form, &hasprecision); - switch (*strfrmt++) { - case 'c': case 'd': case 'i': { - sprintf(buff, form, luaL_checkint(L, arg)); - break; - } - case 'o': case 'u': case 'x': case 'X': { - sprintf(buff, form, (unsigned int)(luaL_checknumber(L, arg))); - break; - } - case 'e': case 'E': case 'f': - case 'g': case 'G': { - sprintf(buff, form, luaL_checknumber(L, arg)); - break; - } - case 'q': { - luaI_addquoted(L, &b, arg); - continue; /* skip the `addsize' at the end */ - } - case 's': { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); - if (!hasprecision && l >= 100) { - /* no precision and string is too long to be formatted; - keep original string */ - lua_pushvalue(L, arg); - luaL_addvalue(&b); - continue; /* skip the `addsize' at the end */ - } - else { - sprintf(buff, form, s); - break; - } - } - default: { /* also treat cases `pnLlh' */ - return luaL_error(L, "invalid option to `format'"); - } - } - luaL_addlstring(&b, buff, strlen(buff)); - } - } - luaL_pushresult(&b); - return 1; -} - - -static const luaL_reg strlib[] = { - {"len", str_len}, - {"sub", str_sub}, - {"lower", str_lower}, - {"upper", str_upper}, - {"char", str_char}, - {"rep", str_rep}, - {"byte", str_byte}, - {"format", str_format}, - {"dump", str_dump}, - {"find", str_find}, - {"gfind", gfind}, - {"gsub", str_gsub}, - {NULL, NULL} -}; - - -/* -** Open string library -*/ -LUALIB_API int luaopen_string (lua_State *L) { - luaL_openlib(L, LUA_STRLIBNAME, strlib, 0); - return 1; -} - +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include +#include +#include +#include +#include + +#define lstrlib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* macro to `unsign' a character */ +#ifndef uchar +#define uchar(c) ((unsigned char)(c)) +#endif + + +typedef long sint32; /* a signed version for size_t */ + + +static int str_len (lua_State *L) { + size_t l; + luaL_checklstring(L, 1, &l); + lua_pushnumber(L, (lua_Number)l); + return 1; +} + + +static sint32 posrelat (sint32 pos, size_t len) { + /* relative string position: negative means back from end */ + return (pos>=0) ? pos : (sint32)len+pos+1; +} + + +static int str_sub (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + sint32 start = posrelat(luaL_checklong(L, 2), l); + sint32 end = posrelat(luaL_optlong(L, 3, -1), l); + if (start < 1) start = 1; + if (end > (sint32)l) end = (sint32)l; + if (start <= end) + lua_pushlstring(L, s+start-1, end-start+1); + else lua_pushliteral(L, ""); + return 1; +} + + +static int str_lower (lua_State *L) { + size_t l; + size_t i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + luaL_buffinit(L, &b); + for (i=0; i 0) + luaL_addlstring(&b, s, l); + luaL_pushresult(&b); + return 1; +} + + +static int str_byte (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + sint32 pos = posrelat(luaL_optlong(L, 2, 1), l); + if (pos <= 0 || (size_t)(pos) > l) /* index out of range? */ + return 0; /* no answer */ + lua_pushnumber(L, uchar(s[pos-1])); + return 1; +} + + +static int str_char (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + luaL_Buffer b; + luaL_buffinit(L, &b); + for (i=1; i<=n; i++) { + int c = luaL_checkint(L, i); + luaL_argcheck(L, uchar(c) == c, i, "invalid value"); + luaL_putchar(&b, uchar(c)); + } + luaL_pushresult(&b); + return 1; +} + + +static int writer (lua_State *L, const void* b, size_t size, void* B) { + (void)L; + luaL_addlstring((luaL_Buffer*) B, (const char *)b, size); + return 1; +} + + +static int str_dump (lua_State *L) { + luaL_Buffer b; + luaL_checktype(L, 1, LUA_TFUNCTION); + luaL_buffinit(L,&b); + if (!lua_dump(L, writer, &b)) + luaL_error(L, "unable to dump given function"); + luaL_pushresult(&b); + return 1; +} + + + +/* +** {====================================================== +** PATTERN MATCHING +** ======================================================= +*/ + +#ifndef MAX_CAPTURES +#define MAX_CAPTURES 32 /* arbitrary limit */ +#endif + + +#define CAP_UNFINISHED (-1) +#define CAP_POSITION (-2) + +typedef struct MatchState { + const char *src_init; /* init of source string */ + const char *src_end; /* end (`\0') of source string */ + lua_State *L; + int level; /* total number of captures (finished or unfinished) */ + struct { + const char *init; + sint32 len; + } capture[MAX_CAPTURES]; +} MatchState; + + +#define ESC '%' +#define SPECIALS "^$*+?.([%-" + + +static int check_capture (MatchState *ms, int l) { + l -= '1'; + if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) + return luaL_error(ms->L, "invalid capture index"); + return l; +} + + +static int capture_to_close (MatchState *ms) { + int level = ms->level; + for (level--; level>=0; level--) + if (ms->capture[level].len == CAP_UNFINISHED) return level; + return luaL_error(ms->L, "invalid pattern capture"); +} + + +static const char *luaI_classend (MatchState *ms, const char *p) { + switch (*p++) { + case ESC: { + if (*p == '\0') + luaL_error(ms->L, "malformed pattern (ends with `%')"); + return p+1; + } + case '[': { + if (*p == '^') p++; + do { /* look for a `]' */ + if (*p == '\0') + luaL_error(ms->L, "malformed pattern (missing `]')"); + if (*(p++) == ESC && *p != '\0') + p++; /* skip escapes (e.g. `%]') */ + } while (*p != ']'); + return p+1; + } + default: { + return p; + } + } +} + + +static int match_class (int c, int cl) { + int res; + switch (tolower(cl)) { + case 'a' : res = isalpha(c); break; + case 'c' : res = iscntrl(c); break; + case 'd' : res = isdigit(c); break; + case 'l' : res = islower(c); break; + case 'p' : res = ispunct(c); break; + case 's' : res = isspace(c); break; + case 'u' : res = isupper(c); break; + case 'w' : res = isalnum(c); break; + case 'x' : res = isxdigit(c); break; + case 'z' : res = (c == 0); break; + default: return (cl == c); + } + return (islower(cl) ? res : !res); +} + + +static int matchbracketclass (int c, const char *p, const char *ec) { + int sig = 1; + if (*(p+1) == '^') { + sig = 0; + p++; /* skip the `^' */ + } + while (++p < ec) { + if (*p == ESC) { + p++; + if (match_class(c, *p)) + return sig; + } + else if ((*(p+1) == '-') && (p+2 < ec)) { + p+=2; + if (uchar(*(p-2)) <= c && c <= uchar(*p)) + return sig; + } + else if (uchar(*p) == c) return sig; + } + return !sig; +} + + +static int luaI_singlematch (int c, const char *p, const char *ep) { + switch (*p) { + case '.': return 1; /* matches any char */ + case ESC: return match_class(c, *(p+1)); + case '[': return matchbracketclass(c, p, ep-1); + default: return (uchar(*p) == c); + } +} + + +static const char *match (MatchState *ms, const char *s, const char *p); + + +static const char *matchbalance (MatchState *ms, const char *s, + const char *p) { + if (*p == 0 || *(p+1) == 0) + luaL_error(ms->L, "unbalanced pattern"); + if (*s != *p) return NULL; + else { + int b = *p; + int e = *(p+1); + int cont = 1; + while (++s < ms->src_end) { + if (*s == e) { + if (--cont == 0) return s+1; + } + else if (*s == b) cont++; + } + } + return NULL; /* string ends out of balance */ +} + + +static const char *max_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + sint32 i = 0; /* counts maximum expand for item */ + while ((s+i)src_end && luaI_singlematch(uchar(*(s+i)), p, ep)) + i++; + /* keeps trying to match with the maximum repetitions */ + while (i>=0) { + const char *res = match(ms, (s+i), ep+1); + if (res) return res; + i--; /* else didn't match; reduce 1 repetition to try again */ + } + return NULL; +} + + +static const char *min_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + for (;;) { + const char *res = match(ms, s, ep+1); + if (res != NULL) + return res; + else if (ssrc_end && luaI_singlematch(uchar(*s), p, ep)) + s++; /* try with one more repetition */ + else return NULL; + } +} + + +static const char *start_capture (MatchState *ms, const char *s, + const char *p, int what) { + const char *res; + int level = ms->level; + if (level >= MAX_CAPTURES) luaL_error(ms->L, "too many captures"); + ms->capture[level].init = s; + ms->capture[level].len = what; + ms->level = level+1; + if ((res=match(ms, s, p)) == NULL) /* match failed? */ + ms->level--; /* undo capture */ + return res; +} + + +static const char *end_capture (MatchState *ms, const char *s, + const char *p) { + int l = capture_to_close(ms); + const char *res; + ms->capture[l].len = s - ms->capture[l].init; /* close capture */ + if ((res = match(ms, s, p)) == NULL) /* match failed? */ + ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ + return res; +} + + +static const char *match_capture (MatchState *ms, const char *s, int l) { + size_t len; + l = check_capture(ms, l); + len = ms->capture[l].len; + if ((size_t)(ms->src_end-s) >= len && + memcmp(ms->capture[l].init, s, len) == 0) + return s+len; + else return NULL; +} + + +static const char *match (MatchState *ms, const char *s, const char *p) { + init: /* using goto's to optimize tail recursion */ + switch (*p) { + case '(': { /* start capture */ + if (*(p+1) == ')') /* position capture? */ + return start_capture(ms, s, p+2, CAP_POSITION); + else + return start_capture(ms, s, p+1, CAP_UNFINISHED); + } + case ')': { /* end capture */ + return end_capture(ms, s, p+1); + } + case ESC: { + switch (*(p+1)) { + case 'b': { /* balanced string? */ + s = matchbalance(ms, s, p+2); + if (s == NULL) return NULL; + p+=4; goto init; /* else return match(ms, s, p+4); */ + } + case 'f': { /* frontier? */ + const char *ep; char previous; + p += 2; + if (*p != '[') + luaL_error(ms->L, "missing `[' after `%%f' in pattern"); + ep = luaI_classend(ms, p); /* points to what is next */ + previous = (s == ms->src_init) ? '\0' : *(s-1); + if (matchbracketclass(uchar(previous), p, ep-1) || + !matchbracketclass(uchar(*s), p, ep-1)) return NULL; + p=ep; goto init; /* else return match(ms, s, ep); */ + } + default: { + if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */ + s = match_capture(ms, s, *(p+1)); + if (s == NULL) return NULL; + p+=2; goto init; /* else return match(ms, s, p+2) */ + } + goto dflt; /* case default */ + } + } + } + case '\0': { /* end of pattern */ + return s; /* match succeeded */ + } + case '$': { + if (*(p+1) == '\0') /* is the `$' the last char in pattern? */ + return (s == ms->src_end) ? s : NULL; /* check end of string */ + else goto dflt; + } + default: dflt: { /* it is a pattern item */ + const char *ep = luaI_classend(ms, p); /* points to what is next */ + int m = ssrc_end && luaI_singlematch(uchar(*s), p, ep); + switch (*ep) { + case '?': { /* optional */ + const char *res; + if (m && ((res=match(ms, s+1, ep+1)) != NULL)) + return res; + p=ep+1; goto init; /* else return match(ms, s, ep+1); */ + } + case '*': { /* 0 or more repetitions */ + return max_expand(ms, s, p, ep); + } + case '+': { /* 1 or more repetitions */ + return (m ? max_expand(ms, s+1, p, ep) : NULL); + } + case '-': { /* 0 or more repetitions (minimum) */ + return min_expand(ms, s, p, ep); + } + default: { + if (!m) return NULL; + s++; p=ep; goto init; /* else return match(ms, s+1, ep); */ + } + } + } + } +} + + + +static const char *lmemfind (const char *s1, size_t l1, + const char *s2, size_t l2) { + if (l2 == 0) return s1; /* empty strings are everywhere */ + else if (l2 > l1) return NULL; /* avoids a negative `l1' */ + else { + const char *init; /* to search for a `*s2' inside `s1' */ + l2--; /* 1st char will be checked by `memchr' */ + l1 = l1-l2; /* `s2' cannot be found after that */ + while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { + init++; /* 1st char is already checked */ + if (memcmp(init, s2+1, l2) == 0) + return init-1; + else { /* correct `l1' and `s1' to try again */ + l1 -= init-s1; + s1 = init; + } + } + return NULL; /* not found */ + } +} + + +static void push_onecapture (MatchState *ms, int i) { + int l = ms->capture[i].len; + if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); + if (l == CAP_POSITION) + lua_pushnumber(ms->L, (lua_Number)(ms->capture[i].init - ms->src_init + 1)); + else + lua_pushlstring(ms->L, ms->capture[i].init, l); +} + + +static int push_captures (MatchState *ms, const char *s, const char *e) { + int i; + luaL_checkstack(ms->L, ms->level, "too many captures"); + if (ms->level == 0 && s) { /* no explicit captures? */ + lua_pushlstring(ms->L, s, e-s); /* return whole match */ + return 1; + } + else { /* return all captures */ + for (i=0; ilevel; i++) + push_onecapture(ms, i); + return ms->level; /* number of strings pushed */ + } +} + + +static int str_find (lua_State *L) { + size_t l1, l2; + const char *s = luaL_checklstring(L, 1, &l1); + const char *p = luaL_checklstring(L, 2, &l2); + sint32 init = posrelat(luaL_optlong(L, 3, 1), l1) - 1; + if (init < 0) init = 0; + else if ((size_t)(init) > l1) init = (sint32)l1; + if (lua_toboolean(L, 4) || /* explicit request? */ + strpbrk(p, SPECIALS) == NULL) { /* or no special characters? */ + /* do a plain search */ + const char *s2 = lmemfind(s+init, l1-init, p, l2); + if (s2) { + lua_pushnumber(L, (lua_Number)(s2-s+1)); + lua_pushnumber(L, (lua_Number)(s2-s+l2)); + return 2; + } + } + else { + MatchState ms; + int anchor = (*p == '^') ? (p++, 1) : 0; + const char *s1=s+init; + ms.L = L; + ms.src_init = s; + ms.src_end = s+l1; + do { + const char *res; + ms.level = 0; + if ((res=match(&ms, s1, p)) != NULL) { + lua_pushnumber(L, (lua_Number)(s1-s+1)); /* start */ + lua_pushnumber(L, (lua_Number)(res-s)); /* end */ + return push_captures(&ms, NULL, 0) + 2; + } + } while (s1++L; + if (lua_isstring(L, 3)) { + const char *news = lua_tostring(L, 3); + size_t l = lua_strlen(L, 3); + size_t i; + for (i=0; i= 3 && (lua_isstring(L, 3) || lua_isfunction(L, 3)), + 3, "string or function expected"); + luaL_buffinit(L, &b); + ms.L = L; + ms.src_init = src; + ms.src_end = src+srcl; + while (n < max_s) { + const char *e; + ms.level = 0; + e = match(&ms, src, p); + if (e) { + n++; + add_s(&ms, &b, src, e); + } + if (e && e>src) /* non empty match? */ + src = e; /* skip it */ + else if (src < ms.src_end) + luaL_putchar(&b, *src++); + else break; + if (anchor) break; + } + luaL_addlstring(&b, src, ms.src_end-src); + luaL_pushresult(&b); + lua_pushnumber(L, (lua_Number)n); /* number of substitutions */ + return 2; +} + +/* }====================================================== */ + + +/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ +#define MAX_ITEM 512 +/* maximum size of each format specification (such as '%-099.99d') */ +#define MAX_FORMAT 20 + + +static void luaI_addquoted (lua_State *L, luaL_Buffer *b, int arg) { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + luaL_putchar(b, '"'); + while (l--) { + switch (*s) { + case '"': case '\\': case '\n': { + luaL_putchar(b, '\\'); + luaL_putchar(b, *s); + break; + } + case '\0': { + luaL_addlstring(b, "\\000", 4); + break; + } + default: { + luaL_putchar(b, *s); + break; + } + } + s++; + } + luaL_putchar(b, '"'); +} + + +static const char *scanformat (lua_State *L, const char *strfrmt, + char *form, int *hasprecision) { + const char *p = strfrmt; + while (strchr("-+ #0", *p)) p++; /* skip flags */ + if (isdigit(uchar(*p))) p++; /* skip width */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + if (*p == '.') { + p++; + *hasprecision = 1; + if (isdigit(uchar(*p))) p++; /* skip precision */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + } + if (isdigit(uchar(*p))) + luaL_error(L, "invalid format (width or precision too long)"); + if (p-strfrmt+2 > MAX_FORMAT) /* +2 to include `%' and the specifier */ + luaL_error(L, "invalid format (too long)"); + form[0] = '%'; + strncpy(form+1, strfrmt, p-strfrmt+1); + form[p-strfrmt+2] = 0; + return p; +} + + +static int str_format (lua_State *L) { + int arg = 1; + size_t sfl; + const char *strfrmt = luaL_checklstring(L, arg, &sfl); + const char *strfrmt_end = strfrmt+sfl; + luaL_Buffer b; + luaL_buffinit(L, &b); + while (strfrmt < strfrmt_end) { + if (*strfrmt != '%') + luaL_putchar(&b, *strfrmt++); + else if (*++strfrmt == '%') + luaL_putchar(&b, *strfrmt++); /* %% */ + else { /* format item */ + char form[MAX_FORMAT]; /* to store the format (`%...') */ + char buff[MAX_ITEM]; /* to store the formatted item */ + int hasprecision = 0; + if (isdigit(uchar(*strfrmt)) && *(strfrmt+1) == '$') + return luaL_error(L, "obsolete option (d$) to `format'"); + arg++; + strfrmt = scanformat(L, strfrmt, form, &hasprecision); + switch (*strfrmt++) { + case 'c': case 'd': case 'i': { + sprintf(buff, form, luaL_checkint(L, arg)); + break; + } + case 'o': case 'u': case 'x': case 'X': { + sprintf(buff, form, (unsigned int)(luaL_checknumber(L, arg))); + break; + } + case 'e': case 'E': case 'f': + case 'g': case 'G': { + sprintf(buff, form, luaL_checknumber(L, arg)); + break; + } + case 'q': { + luaI_addquoted(L, &b, arg); + continue; /* skip the `addsize' at the end */ + } + case 's': { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + if (!hasprecision && l >= 100) { + /* no precision and string is too long to be formatted; + keep original string */ + lua_pushvalue(L, arg); + luaL_addvalue(&b); + continue; /* skip the `addsize' at the end */ + } + else { + sprintf(buff, form, s); + break; + } + } + default: { /* also treat cases `pnLlh' */ + return luaL_error(L, "invalid option to `format'"); + } + } + luaL_addlstring(&b, buff, strlen(buff)); + } + } + luaL_pushresult(&b); + return 1; +} + + +static const luaL_reg strlib[] = { + {"len", str_len}, + {"sub", str_sub}, + {"lower", str_lower}, + {"upper", str_upper}, + {"char", str_char}, + {"rep", str_rep}, + {"byte", str_byte}, + {"format", str_format}, + {"dump", str_dump}, + {"find", str_find}, + {"gfind", gfind}, + {"gsub", str_gsub}, + {NULL, NULL} +}; + + +/* +** Open string library +*/ +LUALIB_API int luaopen_string (lua_State *L) { + luaL_openlib(L, LUA_STRLIBNAME, strlib, 0); + return 1; +} + diff --git a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ltablib.c b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ltablib.c index 50fd282386..2c18bc7f7e 100644 --- a/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ltablib.c +++ b/ryzom/client/src/lua_ide_dll_nevrax/source/standard/ltablib.c @@ -1,260 +1,260 @@ -// Ryzom - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - - -#include - -#define ltablib_c - -#include "lua.h" - -#include "lauxlib.h" -#include "lualib.h" - - -#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n)) - - -static int luaB_foreachi (lua_State *L) { - int i; - int n = aux_getn(L, 1); - luaL_checktype(L, 2, LUA_TFUNCTION); - for (i=1; i<=n; i++) { - lua_pushvalue(L, 2); /* function */ - lua_pushnumber(L, (lua_Number)i); /* 1st argument */ - lua_rawgeti(L, 1, i); /* 2nd argument */ - lua_call(L, 2, 1); - if (!lua_isnil(L, -1)) - return 1; - lua_pop(L, 1); /* remove nil result */ - } - return 0; -} - - -static int luaB_foreach (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - luaL_checktype(L, 2, LUA_TFUNCTION); - lua_pushnil(L); /* first key */ - for (;;) { - if (lua_next(L, 1) == 0) - return 0; - lua_pushvalue(L, 2); /* function */ - lua_pushvalue(L, -3); /* key */ - lua_pushvalue(L, -3); /* value */ - lua_call(L, 2, 1); - if (!lua_isnil(L, -1)) - return 1; - lua_pop(L, 2); /* remove value and result */ - } -} - - -static int luaB_getn (lua_State *L) { - lua_pushnumber(L, (lua_Number)aux_getn(L, 1)); - return 1; -} - - -static int luaB_setn (lua_State *L) { - luaL_checktype(L, 1, LUA_TTABLE); - luaL_setn(L, 1, luaL_checkint(L, 2)); - return 0; -} - - -static int luaB_tinsert (lua_State *L) { - int v = lua_gettop(L); /* number of arguments */ - int n = aux_getn(L, 1) + 1; - int pos; /* where to insert new element */ - if (v == 2) /* called with only 2 arguments */ - pos = n; /* insert new element at the end */ - else { - pos = luaL_checkint(L, 2); /* 2nd argument is the position */ - if (pos > n) n = pos; /* `grow' array if necessary */ - v = 3; /* function may be called with more than 3 args */ - } - luaL_setn(L, 1, n); /* new size */ - while (--n >= pos) { /* move up elements */ - lua_rawgeti(L, 1, n); - lua_rawseti(L, 1, n+1); /* t[n+1] = t[n] */ - } - lua_pushvalue(L, v); - lua_rawseti(L, 1, pos); /* t[pos] = v */ - return 0; -} - - -static int luaB_tremove (lua_State *L) { - int n = aux_getn(L, 1); - int pos = luaL_optint(L, 2, n); - if (n <= 0) return 0; /* table is `empty' */ - luaL_setn(L, 1, n-1); /* t.n = n-1 */ - lua_rawgeti(L, 1, pos); /* result = t[pos] */ - for ( ;pos= P */ - while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { - if (i>u) luaL_error(L, "invalid order function for sorting"); - lua_pop(L, 1); /* remove a[i] */ - } - /* repeat --j until a[j] <= P */ - while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { - if (j +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#include + +#define ltablib_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n)) + + +static int luaB_foreachi (lua_State *L) { + int i; + int n = aux_getn(L, 1); + luaL_checktype(L, 2, LUA_TFUNCTION); + for (i=1; i<=n; i++) { + lua_pushvalue(L, 2); /* function */ + lua_pushnumber(L, (lua_Number)i); /* 1st argument */ + lua_rawgeti(L, 1, i); /* 2nd argument */ + lua_call(L, 2, 1); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 1); /* remove nil result */ + } + return 0; +} + + +static int luaB_foreach (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checktype(L, 2, LUA_TFUNCTION); + lua_pushnil(L); /* first key */ + for (;;) { + if (lua_next(L, 1) == 0) + return 0; + lua_pushvalue(L, 2); /* function */ + lua_pushvalue(L, -3); /* key */ + lua_pushvalue(L, -3); /* value */ + lua_call(L, 2, 1); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 2); /* remove value and result */ + } +} + + +static int luaB_getn (lua_State *L) { + lua_pushnumber(L, (lua_Number)aux_getn(L, 1)); + return 1; +} + + +static int luaB_setn (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_setn(L, 1, luaL_checkint(L, 2)); + return 0; +} + + +static int luaB_tinsert (lua_State *L) { + int v = lua_gettop(L); /* number of arguments */ + int n = aux_getn(L, 1) + 1; + int pos; /* where to insert new element */ + if (v == 2) /* called with only 2 arguments */ + pos = n; /* insert new element at the end */ + else { + pos = luaL_checkint(L, 2); /* 2nd argument is the position */ + if (pos > n) n = pos; /* `grow' array if necessary */ + v = 3; /* function may be called with more than 3 args */ + } + luaL_setn(L, 1, n); /* new size */ + while (--n >= pos) { /* move up elements */ + lua_rawgeti(L, 1, n); + lua_rawseti(L, 1, n+1); /* t[n+1] = t[n] */ + } + lua_pushvalue(L, v); + lua_rawseti(L, 1, pos); /* t[pos] = v */ + return 0; +} + + +static int luaB_tremove (lua_State *L) { + int n = aux_getn(L, 1); + int pos = luaL_optint(L, 2, n); + if (n <= 0) return 0; /* table is `empty' */ + luaL_setn(L, 1, n-1); /* t.n = n-1 */ + lua_rawgeti(L, 1, pos); /* result = t[pos] */ + for ( ;pos= P */ + while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (i>u) luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[i] */ + } + /* repeat --j until a[j] <= P */ + while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { + if (j */ -body { background: #FFFFFF; color: #000000; margin-left: 20px; margin-right: 20px; } -body { background: #FFFFFF; color: #000000; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 70%; margin: 0; } -caption, a.elref, a.elref, caption { font-weight: bold; } -div.ah { background-color: #000000; color: #FFFFFF; font-weight: bold; margin-bottom: 3px; margin-top: 3px; } -div.ah{ background: #000000; color: #FFFFFF; font-weight: bold; margin-bottom: 3px; margin-top: 3px; } -div.footer{ background: #B0C2D7; border: 1px solid #436976; color: #000000; font-style: normal; margin-top: 5em; padding-bottom: 3px; text-align: center; } -div.fragment { background-color: #F5F5F5; border: 1px solid #CCCCCC; margin: 4px; padding-left: 4px; width: 98%; } -div.fragment{ background: #FFFFCC; border: 1px solid #CCCCCC; margin-left: 2em; margin-right: 2em; } -pre { font-size: 120%; margin:2px 1px 3px 1px; } -div.groupheader { font-weight: bold; margin-bottom: 6px; margin-left: 16px; margin-top: 12px; } -div.groupheader{ border-bottom: 1px solid #557E8C; font-weight: bold; margin-bottom: 6px; margin-left: 16px; margin-top: 12px; } -div.grouptext{ font-style: italic; margin-left: 16px; } -div.header{ background: #B0C2D7; border: 1px solid #436976; color: #000000; font-style: normal; margin-bottom: 1em; padding-bottom: 3px; padding-left: 10px; text-align: left; text-transform: lowercase; } -div.header a, div.footer a{ color: #436976; font-weight: bold; white-space: nowrap; } -div.header a {margin-right: 1em; } -div.header a:visited, div.footer a:visited { color: #436976; font-weight: bold; } -div.header a:hover, div.footer a:hover { color: #B52010; font-weight: bold; text-decoration: none; } -div.index { padding-right: 10px; text-align: right; } -div.maindox { margin: 2%; width: 96%; } -div.nav { float: left; } -div.qindex { background-color: #EEEEFF; border: 4px solid #EEEEFF; margin-bottom: 2px; text-align: center; width: 100%; } -dl.el { margin-left: -1cm; } -font.charliteral, span.charliteral { color: #008080; } -font.comment, span.comment { color: #800000; } -font.keyword, span.keyword { color: #008000; } -font.keywordflow, span.keywordflow { color: #E08000; } -font.keywordtype, span.keywordtype { color: #604020; } -font.preprocessor, span.preprocessor { color: #806020; } -font.stringliteral, span.stringliteral { color: #002080; } -h1{ font-size: 220%; text-align: center; } -h2{ border-bottom: 2px solid #557E8C; font-size: 170%; } -hr { border: 1px solid #000000; } -img.formuladsp { } -img.formulainl { vertical-align: middle; } -p.formuladsp { text-align: center; } -td { font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 70%; } -td.indexkey{ background: #EEEEFF; font-weight: bold; margin: 2px 0px; padding: 2px 10px; } -td.indexvalue{ background: #EEEEFF; font-style: italic; margin: 2px 0px; padding: 2px 10px; } -td.md{ font-weight: bold; } -td.mdname, td.mdname1 { color: #FF9900; font-weight: bold; } -tr.memlist { background-color: #F0F0F0; } -ul { list-style-type: square; } -.mdescleft { background-color: #FAFAFA; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-top: 1px none #E0E0E0; margin: 0px; padding-left: 8px; } -.mdescright { background-color: #FAFAFA; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-top: 1px none #E0E0E0; font-style: italic; margin: 0px; padding-bottom: 0px; padding-left: 4px; padding-right: 8px; } -.mdrow { padding: 8px 20px; } -.mdtable { background: #EEEEEE; border: 1px solid #CCCCCC; } -.memitemleft { background-color: #FAFAFA; border-top-color: #FFFFFF; border-top-style: solid; border-top-width: 2px; font-family: Geneva, Arial, Helvetica, sans-serif; font-size: 12px; margin: 4px; padding: 1px 0px 0px 8px; } -.memitemright { background-color: #FAFAFA; border-top-color: #FFFFFF; border-top-style: solid; border-top-width: 2px; font-family: Geneva, Arial, Helvetica, sans-serif; font-size: 13px; margin: 4px; padding: 1px 0px 0px 8px; } +a.code { color: #4444EE; font-weight: normal; text-decoration: none; } +a.coderef { color: #4444EE; font-weight: normal; } +a:link { color: #0066CC; text-decoration: none; } +a:visited { color: #436976; text-decoration: none; } +a.code{ color: #436976; font-weight: normal; text-decoration: none; } +a.coderef{ color: #436976; font-weight: normal; } +a.qindex{ font-weight: normal; white-space: nowrap; } +a.qindex:hover, a.qindexref:hover { background-color: #DDDDFF; text-decoration: none; } +a.qindexhl, a.qindexrefhl { background-color: #6666CC; color: #FFFFFF; font-weight: bold; text-decoration: none; } +a.qindexhl:hover, a.qindexrefhl:hover { background-color: #6666CC; text-decoration: none; } +a.qindexref{ } +a:hover { color: #B52010; color: #B52010; text-decoration: underline; text-decoration: underline; } +a.el, a.qindex, a.qindexref, a.el { font-weight: bold; text-decoration: none; } +/* this is repeated to fix a difference in browser rendering, perhaps should be set for each class of */ +body { background: #FFFFFF; color: #000000; margin-left: 20px; margin-right: 20px; } +body { background: #FFFFFF; color: #000000; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 70%; margin: 0; } +caption, a.elref, a.elref, caption { font-weight: bold; } +div.ah { background-color: #000000; color: #FFFFFF; font-weight: bold; margin-bottom: 3px; margin-top: 3px; } +div.ah{ background: #000000; color: #FFFFFF; font-weight: bold; margin-bottom: 3px; margin-top: 3px; } +div.footer{ background: #B0C2D7; border: 1px solid #436976; color: #000000; font-style: normal; margin-top: 5em; padding-bottom: 3px; text-align: center; } +div.fragment { background-color: #F5F5F5; border: 1px solid #CCCCCC; margin: 4px; padding-left: 4px; width: 98%; } +div.fragment{ background: #FFFFCC; border: 1px solid #CCCCCC; margin-left: 2em; margin-right: 2em; } +pre { font-size: 120%; margin:2px 1px 3px 1px; } +div.groupheader { font-weight: bold; margin-bottom: 6px; margin-left: 16px; margin-top: 12px; } +div.groupheader{ border-bottom: 1px solid #557E8C; font-weight: bold; margin-bottom: 6px; margin-left: 16px; margin-top: 12px; } +div.grouptext{ font-style: italic; margin-left: 16px; } +div.header{ background: #B0C2D7; border: 1px solid #436976; color: #000000; font-style: normal; margin-bottom: 1em; padding-bottom: 3px; padding-left: 10px; text-align: left; text-transform: lowercase; } +div.header a, div.footer a{ color: #436976; font-weight: bold; white-space: nowrap; } +div.header a {margin-right: 1em; } +div.header a:visited, div.footer a:visited { color: #436976; font-weight: bold; } +div.header a:hover, div.footer a:hover { color: #B52010; font-weight: bold; text-decoration: none; } +div.index { padding-right: 10px; text-align: right; } +div.maindox { margin: 2%; width: 96%; } +div.nav { float: left; } +div.qindex { background-color: #EEEEFF; border: 4px solid #EEEEFF; margin-bottom: 2px; text-align: center; width: 100%; } +dl.el { margin-left: -1cm; } +font.charliteral, span.charliteral { color: #008080; } +font.comment, span.comment { color: #800000; } +font.keyword, span.keyword { color: #008000; } +font.keywordflow, span.keywordflow { color: #E08000; } +font.keywordtype, span.keywordtype { color: #604020; } +font.preprocessor, span.preprocessor { color: #806020; } +font.stringliteral, span.stringliteral { color: #002080; } +h1{ font-size: 220%; text-align: center; } +h2{ border-bottom: 2px solid #557E8C; font-size: 170%; } +hr { border: 1px solid #000000; } +img.formuladsp { } +img.formulainl { vertical-align: middle; } +p.formuladsp { text-align: center; } +td { font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; font-size: 70%; } +td.indexkey{ background: #EEEEFF; font-weight: bold; margin: 2px 0px; padding: 2px 10px; } +td.indexvalue{ background: #EEEEFF; font-style: italic; margin: 2px 0px; padding: 2px 10px; } +td.md{ font-weight: bold; } +td.mdname, td.mdname1 { color: #FF9900; font-weight: bold; } +tr.memlist { background-color: #F0F0F0; } +ul { list-style-type: square; } +.mdescleft { background-color: #FAFAFA; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-top: 1px none #E0E0E0; margin: 0px; padding-left: 8px; } +.mdescright { background-color: #FAFAFA; border-bottom: 1px none #E0E0E0; border-left: 1px none #E0E0E0; border-right: 1px none #E0E0E0; border-top: 1px none #E0E0E0; font-style: italic; margin: 0px; padding-bottom: 0px; padding-left: 4px; padding-right: 8px; } +.mdrow { padding: 8px 20px; } +.mdtable { background: #EEEEEE; border: 1px solid #CCCCCC; } +.memitemleft { background-color: #FAFAFA; border-top-color: #FFFFFF; border-top-style: solid; border-top-width: 2px; font-family: Geneva, Arial, Helvetica, sans-serif; font-size: 12px; margin: 4px; padding: 1px 0px 0px 8px; } +.memitemright { background-color: #FAFAFA; border-top-color: #FFFFFF; border-top-style: solid; border-top-width: 2px; font-family: Geneva, Arial, Helvetica, sans-serif; font-size: 13px; margin: 4px; padding: 1px 0px 0px 8px; } diff --git a/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_input.h b/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_input.h index 8a5283ed78..c2c32b8f27 100644 --- a/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_input.h +++ b/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_input.h @@ -17,7 +17,7 @@ //------------------------------------------------------------------------------------------------- // project: sadge lib // file: text_input.h -// version: $Id: text_input.h,v 1.2 2004/06/15 17:33:37 boucher Exp $ +// version: $Id$ // //------------------------------------------------------------------------------------------------- diff --git a/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_output.h b/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_output.h index ba77ab9e24..5d236e61e5 100644 --- a/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_output.h +++ b/ryzom/tools/leveldesign/csv_transform/sadge_lib/include/text_output.h @@ -20,7 +20,7 @@ //------------------------------------------------------------------------------------------------- // project: sadge lib // file: text_output.h -// version: $Id: text_output.h,v 1.1 2004/02/10 17:54:46 boucher Exp $ +// version: $Id$ // // This file contains a string class derived from the STL string // The string compare functions od the class are case insensitive From abe102af98365094253c2d98d2f8752ad0f242b3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 22:54:01 +0000 Subject: [PATCH 14/24] Merge core4 branch and apply v[3].w VP bugfix to new decal system Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/src/3d/decal_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nel/src/3d/decal_manager.cpp b/nel/src/3d/decal_manager.cpp index 8244ff6f9b..ed7208681f 100644 --- a/nel/src/3d/decal_manager.cpp +++ b/nel/src/3d/decal_manager.cpp @@ -85,7 +85,7 @@ static const char *DecalAttenuationVertexProgramCode = MAX R1.x, R1.x, c[7].z;\n\ MUL R0.w, R1.x, R0.w;\n\ #apply vertex alpha\n\ - MUL o[COL0].w, v[3], R0.w;\n\ + MUL o[COL0].w, v[3].w, R0.w;\n\ END \n"; From 5e31092ce3bd93abad089b6f9d6c4e145a1287f8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 00:19:44 +0000 Subject: [PATCH 15/24] Phase 5: Add contains() hit-test, ClientCfg.NewDecalSystem toggle, conditional legacy rendering Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/include/nel/3d/decal.h | 7 +++++++ nel/include/nel/3d/u_decal.h | 3 +++ nel/src/3d/decal.cpp | 10 ++++++++++ nel/src/3d/u_decal.cpp | 7 +++++++ ryzom/client/src/client_cfg.cpp | 5 +++++ ryzom/client/src/client_cfg.h | 3 +++ ryzom/client/src/landscape_poly_drawer.cpp | 9 +++++++-- 7 files changed, 42 insertions(+), 2 deletions(-) diff --git a/nel/include/nel/3d/decal.h b/nel/include/nel/3d/decal.h index e8fe7d53a5..e9a00d2fad 100644 --- a/nel/include/nel/3d/decal.h +++ b/nel/include/nel/3d/decal.h @@ -254,6 +254,13 @@ class CDecal : public CTransform */ void setWorldMatrixForSpot(const NLMISC::CVector2f &pos, float radius, float angleInRadians = 0.f); + /** Test if a 2D point is contained within this decal's projection area. + * Used by R2 editor tools for hit-testing. + * \param pos 2D point to test (world XY) + * \return true if the point falls within the unit-cube projection + */ + bool contains(const NLMISC::CVector2f &pos) const; + /** Set the diffuse color applied to the decal. * The RGB components tint the texture, alpha is a base opacity. * \param diffuse RGBA diffuse color diff --git a/nel/include/nel/3d/u_decal.h b/nel/include/nel/3d/u_decal.h index 1a9b5e003c..3971d4e433 100644 --- a/nel/include/nel/3d/u_decal.h +++ b/nel/include/nel/3d/u_decal.h @@ -97,6 +97,9 @@ class UDecal : public UTransform /// Set the world matrix for a spot-shaped decal void setWorldMatrixForSpot(const NLMISC::CVector2f &pos, float radius, float angleInRadians = 0.f); + + /// Test if a 2D point is contained within this decal's projection area (for hit-testing) + bool contains(const NLMISC::CVector2f &pos) const; }; diff --git a/nel/src/3d/decal.cpp b/nel/src/3d/decal.cpp index 4059988673..3069aa4299 100644 --- a/nel/src/3d/decal.cpp +++ b/nel/src/3d/decal.cpp @@ -535,3 +535,13 @@ tex->setUploadFormat(ITexture::RGBA8888); _MaskTexture = tex; return _MaskTexture; } + + +// *************************************************************************** +bool CDecal::contains(const NLMISC::CVector2f &pos) const +{ + CMatrix invMat = getWorldMatrix(); + invMat.invert(); + CVector posIn = invMat * CVector(pos.x, pos.y, 0.f); + return posIn.x >= 0.f && posIn.x <= 1.f && posIn.y >= 0.f && posIn.y <= 1.f; +} diff --git a/nel/src/3d/u_decal.cpp b/nel/src/3d/u_decal.cpp index 0bb7f49ba1..64fd907ffe 100644 --- a/nel/src/3d/u_decal.cpp +++ b/nel/src/3d/u_decal.cpp @@ -142,4 +142,11 @@ void UDecal::setWorldMatrixForSpot(const NLMISC::CVector2f &pos, float radius, f object->setWorldMatrixForSpot(pos, radius, angleInRadians); } +// *************************************************************************** +bool UDecal::contains(const NLMISC::CVector2f &pos) const +{ + CDecal *object = (CDecal*)_Object; + return object->contains(pos); +} + } // NL3D diff --git a/ryzom/client/src/client_cfg.cpp b/ryzom/client/src/client_cfg.cpp index 689410768f..e50127bc61 100644 --- a/ryzom/client/src/client_cfg.cpp +++ b/ryzom/client/src/client_cfg.cpp @@ -405,6 +405,8 @@ CClientConfig::CClientConfig() SquareBloom = true; DensityBloom = 255.f; + NewDecalSystem = false; + GlobalWindPower = 0.10f; // Default is 0.25 GlobalWindDirection = CVector(1,0,0); // Default direction is X>0 @@ -1029,6 +1031,9 @@ void CClientConfig::setValues() READ_BOOL_FV(SquareBloom) READ_FLOAT_FV(DensityBloom) + // Decal system + READ_BOOL_FV(NewDecalSystem) + // FXAA READ_BOOL_FV(FXAA) diff --git a/ryzom/client/src/client_cfg.h b/ryzom/client/src/client_cfg.h index 2a36ab1a14..64699bf509 100644 --- a/ryzom/client/src/client_cfg.h +++ b/ryzom/client/src/client_cfg.h @@ -299,6 +299,9 @@ struct CClientConfig bool SquareBloom; float DensityBloom; + /// Use the new engine-level decal system instead of the legacy client-side one + bool NewDecalSystem; + /// Movie Shooter uint MovieShooterMemory; string MovieShooterPath; diff --git a/ryzom/client/src/landscape_poly_drawer.cpp b/ryzom/client/src/landscape_poly_drawer.cpp index 1b33a55e60..b2e6fab585 100644 --- a/ryzom/client/src/landscape_poly_drawer.cpp +++ b/ryzom/client/src/landscape_poly_drawer.cpp @@ -28,6 +28,7 @@ // client #include "legacy_decal.h" +#include "client_cfg.h" using namespace NLMISC; using namespace NL3D; @@ -309,8 +310,12 @@ void CLandscapePolyDrawer::renderLandscapePolyPart() // render decals on landscape only Driver->stencilFunc(UDriver::equal, 0x80, 0x80); - // call to render the decals just before the projected polygons on landscape - CLegacyDecalRenderList::getInstance().renderAllDecals(); + // call to render the legacy decals just before the projected polygons on landscape + // (when new decal system is active, rendering is handled by CDecalManager::flush() in render traversal) + if (!ClientCfg.NewDecalSystem) + { + CLegacyDecalRenderList::getInstance().renderAllDecals(); + } // disable stencil test Driver->enableStencilTest(false); From dc3809896b45ebe0b8547c7f8ba7770e45d14cc5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 01:45:12 +0000 Subject: [PATCH 16/24] Fix ambiguous symbol: rename legacy CVertexProgramDecalAttenuation to CLegacyVertexProgramDecalAttenuation Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- ryzom/client/src/legacy_decal.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ryzom/client/src/legacy_decal.cpp b/ryzom/client/src/legacy_decal.cpp index 37893f4fcf..6ffe02274a 100644 --- a/ryzom/client/src/legacy_decal.cpp +++ b/ryzom/client/src/legacy_decal.cpp @@ -92,7 +92,7 @@ static const char *DecalAttenuationVertexProgramCode = MUL o[COL0].w, v[3].w, R0.w; \n\ END \n"; -class CVertexProgramDecalAttenuation : public CVertexProgram +class CLegacyVertexProgramDecalAttenuation : public CVertexProgram { public: struct CIdx @@ -107,7 +107,7 @@ class CVertexProgramDecalAttenuation : public CVertexProgram // 10 uint BlendScale; // 11 }; - CVertexProgramDecalAttenuation() + CLegacyVertexProgramDecalAttenuation() { // nelvp { @@ -126,7 +126,7 @@ class CVertexProgramDecalAttenuation : public CVertexProgram } // TODO_VP_GLSL } - ~CVertexProgramDecalAttenuation() + ~CLegacyVertexProgramDecalAttenuation() { } @@ -150,7 +150,7 @@ class CVertexProgramDecalAttenuation : public CVertexProgram CIdx m_Idx; }; -static NLMISC::CSmartPtr DecalAttenuationVertexProgram; +static NLMISC::CSmartPtr DecalAttenuationVertexProgram; typedef CShadowPolyReceiver::CRGBAVertex CRGBAVertex; @@ -160,7 +160,7 @@ CLegacyDecal::CLegacyDecal() { if (!DecalAttenuationVertexProgram) { - DecalAttenuationVertexProgram = new CVertexProgramDecalAttenuation(); + DecalAttenuationVertexProgram = new CLegacyVertexProgramDecalAttenuation(); } // initialized in render() as depends on scene @@ -379,7 +379,7 @@ void CLegacyDecal::renderTriCache(NL3D::IDriver &drv, NL3D::CShadowPolyReceive drv.setupModelMatrix(modelMat); if (useVertexProgram) { - CVertexProgramDecalAttenuation *program = DecalAttenuationVertexProgram; + CLegacyVertexProgramDecalAttenuation *program = DecalAttenuationVertexProgram; { CVertexBufferReadWrite vba; _VB.setNumVertices((uint32)_TriCache.size()); From cca0dc5e1640e0f4873748e567a12ec35f0cdb9d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 02:14:12 +0000 Subject: [PATCH 17/24] Fix blank polygon rendering: add initUnlit(), set texture on stage 1, use per-decal material in flush Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/src/3d/decal.cpp | 6 ++++-- nel/src/3d/decal_manager.cpp | 20 +++++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/nel/src/3d/decal.cpp b/nel/src/3d/decal.cpp index 3069aa4299..c1ddf5e367 100644 --- a/nel/src/3d/decal.cpp +++ b/nel/src/3d/decal.cpp @@ -71,8 +71,8 @@ setOpacity(true); setTransparency(false); setIsRenderable(true); -// Material setup: alpha-blended, double-sided, no z-write, with z-bias -_Mat.setShader(CMaterial::TShader::Normal); +// Material setup: unlit, alpha-blended, double-sided, no z-write, with z-bias +_Mat.initUnlit(); _Mat.setBlend(true); _Mat.setSrcBlend(CMaterial::srcalpha); _Mat.setDstBlend(CMaterial::invsrcalpha); @@ -135,6 +135,8 @@ tex->setFilterMode(ITexture::Linear, ITexture::LinearMipMapLinear); tex->setWrapS(ITexture::Clamp); tex->setWrapT(ITexture::Clamp); _Mat.setTexture(0, tex); +// Stage 1 also needs the texture for the emissive add operation to work correctly +_Mat.setTexture(1, tex); } diff --git a/nel/src/3d/decal_manager.cpp b/nel/src/3d/decal_manager.cpp index ed7208681f..d5d6b0e653 100644 --- a/nel/src/3d/decal_manager.cpp +++ b/nel/src/3d/decal_manager.cpp @@ -282,6 +282,24 @@ void CDecalManager::flush(CScene *sc) if (verts.empty()) continue; + // Each decal has its own material (with its own texture). + // Flush when the material changes or when using VP (VP constants are per-decal). + CMaterial *decalMat = &decal->getMaterial(); + if (count > 0 && (decalMat != mat || vpActive)) + { + if (vbLocked) + { + vba.unlock(); + vbLocked = false; + } + nlassert(count % 3 == 0); + drv->renderRawTriangles(*mat, 0, count / 3); + count = 0; + } + + // Use this decal's own material (which has its texture set) + mat = decalMat; + // If using VP, set per-decal constants before we start copying vertices if (vpActive) { @@ -388,7 +406,7 @@ void CDecalManager::flush(CScene *sc) } } - // Unlock and render any remaining vertices for this material + // Unlock and render any remaining vertices for the last decal if (vbLocked) { vba.unlock(); From 5fd9225dbcf84d79dfd67806159caff4226455ac Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 14:42:35 +0000 Subject: [PATCH 18/24] Fix texture rendering: remove VP double V-flip, unify CPU/VP UV generation via WorldToUV matrix Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/src/3d/decal.cpp | 27 ++++++++++----------------- nel/src/3d/decal_manager.cpp | 21 +++++++-------------- 2 files changed, 17 insertions(+), 31 deletions(-) diff --git a/nel/src/3d/decal.cpp b/nel/src/3d/decal.cpp index c1ddf5e367..e5c35b9137 100644 --- a/nel/src/3d/decal.cpp +++ b/nel/src/3d/decal.cpp @@ -364,26 +364,19 @@ else _WorldToUVMatrix = _TextureMatrix * reverseUV * invWorld; } +// Use the worldToUV matrix to generate UVs, matching the VP path. +// Row 0 of the matrix gives U, Row 1 gives V. +const float *m = _WorldToUVMatrix.get(); for (uint i = 0; i < _Vertices.size(); ++i) { - float u, v; - if (_CustomUVMatrixEnabled) - { - // Custom matrix outputs UVs directly - CVector uvCoord = _WorldToUVMatrix * _Vertices[i]; - u = uvCoord.x; - v = uvCoord.y; - } - else - { - // Map local X,Y to UV sub-region - CVector local = invWorld * _Vertices[i]; - u = _UV1.U + local.x * (_UV2.U - _UV1.U); - v = _UV1.V + (1.0f - local.y) * (_UV2.V - _UV1.V); - } + const CVector &vtx = _Vertices[i]; + // DP4 equivalent: dot product of matrix row with (vx, vy, vz, 1) + float u = m[0] * vtx.x + m[4] * vtx.y + m[8] * vtx.z + m[12]; + float v = m[1] * vtx.x + m[5] * vtx.y + m[9] * vtx.z + m[13]; - _UVs[i].U = u; - _UVs[i].V = v; + // Apply UV sub-region mapping (for texture atlases) + _UVs[i].U = _UV1.U + u * (_UV2.U - _UV1.U); + _UVs[i].V = _UV1.V + v * (_UV2.V - _UV1.V); } } diff --git a/nel/src/3d/decal_manager.cpp b/nel/src/3d/decal_manager.cpp index d5d6b0e653..ef04204012 100644 --- a/nel/src/3d/decal_manager.cpp +++ b/nel/src/3d/decal_manager.cpp @@ -303,21 +303,14 @@ void CDecalManager::flush(CScene *sc) // If using VP, set per-decal constants before we start copying vertices if (vpActive) { - // WorldToUV: rows of the inverse world matrix for UV generation + // WorldToUV: rows of the worldToUV matrix for UV generation. + // The matrix already includes the reverse UV (V flip) from generateUVs(). + // Extract rows the same way the legacy system does via CMatrix::get() into float[4][4]: + // M[16] is column-major, so row R is at indices [R], [4+R], [8+R], [12+R]. const CMatrix &worldToUV = decal->getWorldToUVMatrix(); - float row0[4], row1[4]; - // Row 0: maps world position to U coordinate - row0[0] = worldToUV.get()[0]; // column 0, row 0 - row0[1] = worldToUV.get()[4]; // column 1, row 0 - row0[2] = worldToUV.get()[8]; // column 2, row 0 - row0[3] = worldToUV.get()[12]; // column 3, row 0 (translation) - // Row 1: maps world position to V coordinate (inverted Y for UV) - row1[0] = -worldToUV.get()[1]; - row1[1] = -worldToUV.get()[5]; - row1[2] = -worldToUV.get()[9]; - row1[3] = 1.0f - (-worldToUV.get()[13]); // offset for V flip - drv->setUniform4f(IDriver::VertexProgram, vp->idx().WorldToUV0, row0[0], row0[1], row0[2], row0[3]); - drv->setUniform4f(IDriver::VertexProgram, vp->idx().WorldToUV1, row1[0], row1[1], row1[2], row1[3]); + const float *m = worldToUV.get(); + drv->setUniform4f(IDriver::VertexProgram, vp->idx().WorldToUV0, m[0], m[4], m[8], m[12]); + drv->setUniform4f(IDriver::VertexProgram, vp->idx().WorldToUV1, m[1], m[5], m[9], m[13]); // Camera position (world space) drv->setUniform4f(IDriver::VertexProgram, vp->idx().RefCamDist, camPos.x, camPos.y, camPos.z, 1.f); From a04c0f2afd3f81ab52d6d51aefdad643433a9524 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 15:14:49 +0000 Subject: [PATCH 19/24] Fix batching efficiency: always CPU-precompute UVs/colors, batch by texture pointer not per-decal VP Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/include/nel/3d/decal_manager.h | 6 ++ nel/src/3d/decal.cpp | 9 +- nel/src/3d/decal_manager.cpp | 149 +++++------------------------ 3 files changed, 32 insertions(+), 132 deletions(-) diff --git a/nel/include/nel/3d/decal_manager.h b/nel/include/nel/3d/decal_manager.h index 460a588497..9f5c44b647 100644 --- a/nel/include/nel/3d/decal_manager.h +++ b/nel/include/nel/3d/decal_manager.h @@ -143,6 +143,12 @@ class CDecalManager */ void setDistAttenuation(float scale, float bias) { _DistScale = scale; _DistBias = bias; } + /// Get distance attenuation scale factor. + float getDistScale() const { return _DistScale; } + + /// Get distance attenuation bias. + float getDistBias() const { return _DistBias; } + private: /// A registered material with its ID struct CRegisteredMaterial diff --git a/nel/src/3d/decal.cpp b/nel/src/3d/decal.cpp index e5c35b9137..68217f6fed 100644 --- a/nel/src/3d/decal.cpp +++ b/nel/src/3d/decal.cpp @@ -327,13 +327,10 @@ lm->Landscape.getShadowPolyReceiver().receiveDecal(context, vertDelta); // Generate UV coordinates from collected vertices generateUVs(); -// Compute per-vertex colors for CPU fallback path -if (!useVertexProgram) -{ +// Always compute per-vertex colors on CPU for batched rendering. +// This allows the manager to batch decals without per-decal VP constant changes. CDecalManager &mgr = sc->getRenderTrav().getDecalManager(); -(void)mgr; // distScale/distBias set externally -computeColors(0.f, 1.f); -} +computeColors(mgr.getDistScale(), mgr.getDistBias()); } diff --git a/nel/src/3d/decal_manager.cpp b/nel/src/3d/decal_manager.cpp index ef04204012..fc46962cbe 100644 --- a/nel/src/3d/decal_manager.cpp +++ b/nel/src/3d/decal_manager.cpp @@ -179,25 +179,12 @@ uint32 CDecalManager::registerMaterial(const CMaterial &mat) } -// *************************************************************************** -// Helper: avoid division by zero for blend scale computation -static inline float decal_favoid0(float f) -{ - return NLMISC::favoid0(f); -} - - // *************************************************************************** // Rendering: implements the pseudocode from PDF §.6.3 // -// With vertex program path: -// - VP handles MVP transform, UV generation, distance attenuation, -// bottom/top Z blending, diffuse color modulation -// - Per-decal VP constants set before each decal's vertices are flushed -// -// Without vertex program (CPU fallback): -// - UVs and per-vertex colors precomputed by CDecal::computeDecal() -// - Written directly into the VB alongside positions +// All UVs and per-vertex colors are precomputed on the CPU by CDecal::computeDecal(). +// The manager batches decals that share the same texture into single draw calls, +// only flushing the VB when the texture pointer changes or the buffer overflows. void CDecalManager::flush(CScene *sc) { if (_Decals.empty()) @@ -205,33 +192,20 @@ void CDecalManager::flush(CScene *sc) IDriver *drv = sc->getRenderTrav().getDriver(); - // Try to compile and activate the vertex program - bool vpActive = false; - CVertexProgramDecalAttenuation *vp = _VertexProgram; - if (_UseVertexProgram && vp && drv->compileVertexProgram(vp)) - { - drv->activeVertexProgram(vp); - // Set distance attenuation constants (shared across all decals) - drv->setUniform4f(IDriver::VertexProgram, vp->idx().DistScaleBias, _DistScale, _DistBias, 0.f, 1.f); - vpActive = true; - } - else - { - drv->activeVertexProgram(NULL); - } - + // No vertex program in the batched path — UVs and colors are CPU-precomputed. + // This is the key to batching: VP constants are per-decal, which would force + // a VB flush per decal, defeating the purpose. CPU precomputation lets us + // batch freely across decals sharing the same texture. + drv->activeVertexProgram(NULL); drv->activeVertexBuffer(_VB); drv->setupModelMatrix(CMatrix::Identity); - const CVector &camPos = sc->getCam()->getMatrix().getPos(); - // Iterate over each material group TDecalMap::iterator matIt = _Decals.begin(); TDecalMap::iterator matEnd = _Decals.end(); for (; matIt != matEnd; ++matIt) { - uint32 materialId = matIt->first; std::vector &decals = matIt->second; if (decals.empty()) @@ -240,52 +214,27 @@ void CDecalManager::flush(CScene *sc) // Sort decals by priority within this material group (lower priority first) std::sort(decals.begin(), decals.end(), decalPriorityCompare); - // Find the registered material for this ID - CMaterial *mat = NULL; - for (uint m = 0; m < _Materials.size(); ++m) - { - if (_Materials[m].Id == materialId) - { - mat = &_Materials[m].Mat; - break; - } - } - - // If no registered material found, use the first decal's material - CMaterial fallbackMat; - if (!mat) - { - if (!decals.empty()) - { - fallbackMat = decals[0]->getMaterial(); - } - else - { - fallbackMat.initUnlit(); - fallbackMat.setDoubleSided(true); - } - mat = &fallbackMat; - } - uint32 count = 0; // current position in VB bool vbLocked = false; CVertexBufferReadWrite vba; + CMaterial *currentMat = NULL; // track current material for batching for (uint d = 0; d < decals.size(); ++d) { CDecal *decal = decals[d]; - // Get this decal's computed vertices and UVs - std::vector &verts = decal->getVertices(vpActive); + // Get this decal's precomputed vertices, UVs, and colors + std::vector &verts = decal->getVertices(false); const std::vector &uvs = decal->getUVs(); + const std::vector &colors = decal->getColors(); if (verts.empty()) continue; - // Each decal has its own material (with its own texture). - // Flush when the material changes or when using VP (VP constants are per-decal). CMaterial *decalMat = &decal->getMaterial(); - if (count > 0 && (decalMat != mat || vpActive)) + + // Flush when the material pointer changes (different texture) + if (count > 0 && decalMat != currentMat) { if (vbLocked) { @@ -293,46 +242,14 @@ void CDecalManager::flush(CScene *sc) vbLocked = false; } nlassert(count % 3 == 0); - drv->renderRawTriangles(*mat, 0, count / 3); + drv->renderRawTriangles(*currentMat, 0, count / 3); count = 0; } - // Use this decal's own material (which has its texture set) - mat = decalMat; - - // If using VP, set per-decal constants before we start copying vertices - if (vpActive) - { - // WorldToUV: rows of the worldToUV matrix for UV generation. - // The matrix already includes the reverse UV (V flip) from generateUVs(). - // Extract rows the same way the legacy system does via CMatrix::get() into float[4][4]: - // M[16] is column-major, so row R is at indices [R], [4+R], [8+R], [12+R]. - const CMatrix &worldToUV = decal->getWorldToUVMatrix(); - const float *m = worldToUV.get(); - drv->setUniform4f(IDriver::VertexProgram, vp->idx().WorldToUV0, m[0], m[4], m[8], m[12]); - drv->setUniform4f(IDriver::VertexProgram, vp->idx().WorldToUV1, m[1], m[5], m[9], m[13]); - - // Camera position (world space) - drv->setUniform4f(IDriver::VertexProgram, vp->idx().RefCamDist, camPos.x, camPos.y, camPos.z, 1.f); - - // Diffuse color (normalized to [0,1]) - CRGBA diff = decal->getDiffuse(); - drv->setUniform4f(IDriver::VertexProgram, vp->idx().Diffuse, diff.R * (1.f / 255.f), diff.G * (1.f / 255.f), diff.B * (1.f / 255.f), 1.f); - - // Bottom & top blend scale/bias - float bottomBlendScale = 1.f / decal_favoid0(decal->getBottomBlendZMax() - decal->getBottomBlendZMin()); - float topBlendScale = 1.f / decal_favoid0(decal->getTopBlendZMin() - decal->getTopBlendZMax()); - drv->setUniform4f(IDriver::VertexProgram, vp->idx().BlendScale, - bottomBlendScale, -bottomBlendScale * decal->getBottomBlendZMin(), - topBlendScale, -topBlendScale * decal->getTopBlendZMax()); - - // MVP matrix - drv->setUniformMatrix(IDriver::VertexProgram, vp->getUniformIndex(CProgramIndex::ModelViewProjection), IDriver::ModelViewProjection, IDriver::Identity); - } + currentMat = decalMat; uint32 length = (uint32)verts.size(); uint32 offset = 0; - const std::vector &colors = decal->getColors(); bool hasColors = (colors.size() == verts.size()); // Ensure VB is locked before copying @@ -355,14 +272,7 @@ void CDecalManager::flush(CScene *sc) *vba.getVertexCoordPointer(count + i) = verts[offset + i]; if (offset + i < uvs.size()) *vba.getTexCoordPointer(count + i, 0) = uvs[offset + i]; - if (!vpActive && hasColors) - { - *(CRGBA *)vba.getColorPointer(count + i) = colors[offset + i]; - } - else - { - *(CRGBA *)vba.getColorPointer(count + i) = CRGBA::White; - } + *(CRGBA *)vba.getColorPointer(count + i) = hasColors ? colors[offset + i] : CRGBA::White; } offset += space; count += space; @@ -371,7 +281,7 @@ void CDecalManager::flush(CScene *sc) vba.unlock(); vbLocked = false; nlassert(count % 3 == 0); - drv->renderRawTriangles(*mat, 0, count / 3); + drv->renderRawTriangles(*currentMat, 0, count / 3); count = 0; _VB.lock(vba); vbLocked = true; @@ -384,14 +294,7 @@ void CDecalManager::flush(CScene *sc) *vba.getVertexCoordPointer(count + i) = verts[offset + i]; if (offset + i < uvs.size()) *vba.getTexCoordPointer(count + i, 0) = uvs[offset + i]; - if (!vpActive && hasColors) - { - *(CRGBA *)vba.getColorPointer(count + i) = colors[offset + i]; - } - else - { - *(CRGBA *)vba.getColorPointer(count + i) = CRGBA::White; - } + *(CRGBA *)vba.getColorPointer(count + i) = hasColors ? colors[offset + i] : CRGBA::White; } count += remaining; offset = length; // done with this decal @@ -399,22 +302,16 @@ void CDecalManager::flush(CScene *sc) } } - // Unlock and render any remaining vertices for the last decal + // Unlock and render any remaining vertices if (vbLocked) { vba.unlock(); vbLocked = false; } - if (count > 0) + if (count > 0 && currentMat) { nlassert(count % 3 == 0); - drv->renderRawTriangles(*mat, 0, count / 3); + drv->renderRawTriangles(*currentMat, 0, count / 3); } } - - // Deactivate vertex program - if (vpActive) - { - drv->activeVertexProgram(NULL); - } } From 10764e5454c3e1f8b3338fa601c9f222dc1d34fd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 15:38:15 +0000 Subject: [PATCH 20/24] Fix landscape vertex coordinate space: shadow poly receiver stores world-space vertices, use CVector::Null vertDelta since we render with Identity model matrix Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/src/3d/decal.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nel/src/3d/decal.cpp b/nel/src/3d/decal.cpp index 68217f6fed..d646fe981f 100644 --- a/nel/src/3d/decal.cpp +++ b/nel/src/3d/decal.cpp @@ -320,8 +320,9 @@ for (uint i = 0; i < landscapes.size(); ++i) { CLandscapeModel *lm = landscapes[i]; if (!lm) continue; -CVector vertDelta = -lm->Landscape.getPZBModelPosition(); -lm->Landscape.getShadowPolyReceiver().receiveDecal(context, vertDelta); +// Shadow poly receiver stores vertices in world space (EndPos from tessellation). +// We render with Identity model matrix, so no offset needed. +lm->Landscape.getShadowPolyReceiver().receiveDecal(context, CVector::Null); } // Generate UV coordinates from collected vertices From 1f30c23a11e0e1b2b8f8c3c2327ea3237feb348d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 16:00:07 +0000 Subject: [PATCH 21/24] Fix decal texture rendering: route stage 1 UV to TexCoord0 via setUVRouting(1, 0) Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/src/3d/decal_manager.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/nel/src/3d/decal_manager.cpp b/nel/src/3d/decal_manager.cpp index fc46962cbe..aa116becf2 100644 --- a/nel/src/3d/decal_manager.cpp +++ b/nel/src/3d/decal_manager.cpp @@ -142,6 +142,11 @@ CDecalManager::CDecalManager() : _VB.setPreferredMemory(CVertexBuffer::AGPVolatile, true); _VB.setVertexFormat(CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag | CVertexBuffer::PrimaryColorFlag); _VB.setNumVertices(NL3D_DECAL_VB_MAX_VERTICES); + // Route texture stage 1 to use TexCoord0 (same as stage 0). + // The material uses 2 texture stages with the same texture, and both + // need valid UVs. Without this, the driver looks for TexCoord1 (not present) + // and disables tex coords for stage 1, zeroing out the alpha. + _VB.setUVRouting(1, 0); // Create the vertex program instance _VertexProgram = new CVertexProgramDecalAttenuation(); From 51cb7c7ea29b64d20ef0501de76ad8fe4da3a6d7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 14 Feb 2026 16:46:44 +0000 Subject: [PATCH 22/24] Fix texture rendering: use GPU tex coord generation matching legacy non-VP path Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/src/3d/decal_manager.cpp | 119 ++++++++++------------------------- 1 file changed, 34 insertions(+), 85 deletions(-) diff --git a/nel/src/3d/decal_manager.cpp b/nel/src/3d/decal_manager.cpp index aa116becf2..3af92b22df 100644 --- a/nel/src/3d/decal_manager.cpp +++ b/nel/src/3d/decal_manager.cpp @@ -138,15 +138,12 @@ CDecalManager::CDecalManager() : _DistScale(0.f), _DistBias(1.f) { - // Fixed-size AGP Volatile vertex buffer with Position + TexCoord0 + PrimaryColor (see PDF §4.5.4) + // Fixed-size AGP Volatile vertex buffer with Position + PrimaryColor (see PDF §4.5.4). + // UV coordinates are generated by the GPU via TexCoordGen + UserTexMat per decal, + // matching the legacy non-VP rendering path. _VB.setPreferredMemory(CVertexBuffer::AGPVolatile, true); - _VB.setVertexFormat(CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag | CVertexBuffer::PrimaryColorFlag); + _VB.setVertexFormat(CVertexBuffer::PositionFlag | CVertexBuffer::PrimaryColorFlag); _VB.setNumVertices(NL3D_DECAL_VB_MAX_VERTICES); - // Route texture stage 1 to use TexCoord0 (same as stage 0). - // The material uses 2 texture stages with the same texture, and both - // need valid UVs. Without this, the driver looks for TexCoord1 (not present) - // and disables tex coords for stage 1, zeroing out the alpha. - _VB.setUVRouting(1, 0); // Create the vertex program instance _VertexProgram = new CVertexProgramDecalAttenuation(); @@ -187,9 +184,11 @@ uint32 CDecalManager::registerMaterial(const CMaterial &mat) // *************************************************************************** // Rendering: implements the pseudocode from PDF §.6.3 // -// All UVs and per-vertex colors are precomputed on the CPU by CDecal::computeDecal(). -// The manager batches decals that share the same texture into single draw calls, -// only flushing the VB when the texture pointer changes or the buffer overflows. +// Per-vertex colors are precomputed on the CPU by CDecal::computeDecal(). +// UV coordinates are generated by the GPU via TexCoordGen + UserTexMat per decal, +// matching the legacy non-VP rendering path. +// Each decal requires its own draw call due to per-decal UV matrix, but +// multiple decals still share the same VB with overflow handling. void CDecalManager::flush(CScene *sc) { if (_Decals.empty()) @@ -197,10 +196,6 @@ void CDecalManager::flush(CScene *sc) IDriver *drv = sc->getRenderTrav().getDriver(); - // No vertex program in the batched path — UVs and colors are CPU-precomputed. - // This is the key to batching: VP constants are per-decal, which would force - // a VB flush per decal, defeating the purpose. CPU precomputation lets us - // batch freely across decals sharing the same texture. drv->activeVertexProgram(NULL); drv->activeVertexBuffer(_VB); drv->setupModelMatrix(CMatrix::Identity); @@ -219,104 +214,58 @@ void CDecalManager::flush(CScene *sc) // Sort decals by priority within this material group (lower priority first) std::sort(decals.begin(), decals.end(), decalPriorityCompare); - uint32 count = 0; // current position in VB - bool vbLocked = false; - CVertexBufferReadWrite vba; - CMaterial *currentMat = NULL; // track current material for batching - for (uint d = 0; d < decals.size(); ++d) { CDecal *decal = decals[d]; - // Get this decal's precomputed vertices, UVs, and colors + // Get this decal's precomputed vertices and colors std::vector &verts = decal->getVertices(false); - const std::vector &uvs = decal->getUVs(); const std::vector &colors = decal->getColors(); if (verts.empty()) continue; - CMaterial *decalMat = &decal->getMaterial(); + CMaterial &mat = decal->getMaterial(); - // Flush when the material pointer changes (different texture) - if (count > 0 && decalMat != currentMat) - { - if (vbLocked) - { - vba.unlock(); - vbLocked = false; - } - nlassert(count % 3 == 0); - drv->renderRawTriangles(*currentMat, 0, count / 3); - count = 0; - } - - currentMat = decalMat; + // Setup GPU texture coordinate generation for this decal, + // matching the legacy non-VP rendering path exactly. + // The GPU generates UVs from vertex positions in object space + // (which equals world space since modelMatrix is Identity), + // then transforms them by the worldToUV matrix. + const CMatrix &worldToUVMatrix = decal->getWorldToUVMatrix(); + mat.enableUserTexMat(0, true); + mat.setTexCoordGen(0, true); + mat.setTexCoordGenMode(0, CMaterial::TexCoordGenObjectSpace); + mat.setUserTexMat(0, worldToUVMatrix); uint32 length = (uint32)verts.size(); uint32 offset = 0; bool hasColors = (colors.size() == verts.size()); - // Ensure VB is locked before copying - if (!vbLocked) - { - _VB.lock(vba); - vbLocked = true; - } - while (offset < length) { uint32 remaining = length - offset; - uint32 space = NL3D_DECAL_VB_MAX_VERTICES - count; + uint32 space = NL3D_DECAL_VB_MAX_VERTICES; + uint32 batch = std::min(remaining, space); - if (remaining > space) { - // Not enough space: fill what we can, render, reset - for (uint32 i = 0; i < space; ++i) - { - *vba.getVertexCoordPointer(count + i) = verts[offset + i]; - if (offset + i < uvs.size()) - *vba.getTexCoordPointer(count + i, 0) = uvs[offset + i]; - *(CRGBA *)vba.getColorPointer(count + i) = hasColors ? colors[offset + i] : CRGBA::White; - } - offset += space; - count += space; - - // Unlock, render, re-lock - vba.unlock(); - vbLocked = false; - nlassert(count % 3 == 0); - drv->renderRawTriangles(*currentMat, 0, count / 3); - count = 0; + CVertexBufferReadWrite vba; _VB.lock(vba); - vbLocked = true; - } - else - { - // Enough space: copy all remaining - for (uint32 i = 0; i < remaining; ++i) + for (uint32 i = 0; i < batch; ++i) { - *vba.getVertexCoordPointer(count + i) = verts[offset + i]; - if (offset + i < uvs.size()) - *vba.getTexCoordPointer(count + i, 0) = uvs[offset + i]; - *(CRGBA *)vba.getColorPointer(count + i) = hasColors ? colors[offset + i] : CRGBA::White; + *vba.getVertexCoordPointer(i) = verts[offset + i]; + vba.setColor(i, hasColors ? colors[offset + i] : CRGBA::White); } - count += remaining; - offset = length; // done with this decal } + + nlassert(batch % 3 == 0); + drv->renderRawTriangles(mat, 0, batch / 3); + offset += batch; } - } - // Unlock and render any remaining vertices - if (vbLocked) - { - vba.unlock(); - vbLocked = false; - } - if (count > 0 && currentMat) - { - nlassert(count % 3 == 0); - drv->renderRawTriangles(*currentMat, 0, count / 3); + // Reset tex coord gen state on the material after rendering + mat.setTexCoordGen(0, false); + mat.enableUserTexMat(0, false); } } } From cfb7662a8f9dc1d668a9df3db09dc7c92ae51d1e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Feb 2026 16:19:37 +0000 Subject: [PATCH 23/24] Fix texture rendering: use CPU-precomputed UVs in VB instead of broken TexCoordGen approach GL_OBJECT_LINEAR with default R/Q planes (0,0,0,0) generates (x,y,0,0) from vertex positions, losing the z and translation (w=1) terms that the worldToUV matrix needs. The legacy system works because it uses the VP path which does DP4 with v[0].w=1. Switch to CPU DP4 in VB TexCoord0. Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/src/3d/decal_manager.cpp | 94 +++++++++++++++++++++++------------- 1 file changed, 61 insertions(+), 33 deletions(-) diff --git a/nel/src/3d/decal_manager.cpp b/nel/src/3d/decal_manager.cpp index 3af92b22df..9826205e26 100644 --- a/nel/src/3d/decal_manager.cpp +++ b/nel/src/3d/decal_manager.cpp @@ -138,14 +138,19 @@ CDecalManager::CDecalManager() : _DistScale(0.f), _DistBias(1.f) { - // Fixed-size AGP Volatile vertex buffer with Position + PrimaryColor (see PDF §4.5.4). - // UV coordinates are generated by the GPU via TexCoordGen + UserTexMat per decal, - // matching the legacy non-VP rendering path. + // Fixed-size AGP Volatile vertex buffer with Position + TexCoord0 + PrimaryColor (see PDF §4.5.4). + // UV coordinates are precomputed on CPU by CDecal::generateUVs() and written to TexCoord0. + // Per-vertex colors are precomputed by CDecal::computeColors(). + // This avoids per-decal VP constant changes and enables true batching by texture. _VB.setPreferredMemory(CVertexBuffer::AGPVolatile, true); - _VB.setVertexFormat(CVertexBuffer::PositionFlag | CVertexBuffer::PrimaryColorFlag); + _VB.setVertexFormat(CVertexBuffer::PositionFlag | CVertexBuffer::TexCoord0Flag | CVertexBuffer::PrimaryColorFlag); _VB.setNumVertices(NL3D_DECAL_VB_MAX_VERTICES); - // Create the vertex program instance + // Route texture stage 1 to read from TexCoord0 (same as stage 0). + // The material uses 2 stages but we only have one UV channel in the VB. + _VB.setUVRouting(1, 0); + + // Create the vertex program instance (retained for potential future use) _VertexProgram = new CVertexProgramDecalAttenuation(); } @@ -184,11 +189,11 @@ uint32 CDecalManager::registerMaterial(const CMaterial &mat) // *************************************************************************** // Rendering: implements the pseudocode from PDF §.6.3 // -// Per-vertex colors are precomputed on the CPU by CDecal::computeDecal(). -// UV coordinates are generated by the GPU via TexCoordGen + UserTexMat per decal, -// matching the legacy non-VP rendering path. -// Each decal requires its own draw call due to per-decal UV matrix, but -// multiple decals still share the same VB with overflow handling. +// Per-vertex colors are precomputed on the CPU by CDecal::computeColors(). +// UV coordinates are precomputed on the CPU by CDecal::generateUVs() and +// written into TexCoord0. This avoids per-decal VP constant updates and +// enables true batching: a draw call is only needed when the texture +// pointer changes or the VB overflows. void CDecalManager::flush(CScene *sc) { if (_Decals.empty()) @@ -214,38 +219,53 @@ void CDecalManager::flush(CScene *sc) // Sort decals by priority within this material group (lower priority first) std::sort(decals.begin(), decals.end(), decalPriorityCompare); + // Track current texture for batching: flush only on texture change or VB overflow + ITexture *curTex = NULL; + uint32 vbOffset = 0; + for (uint d = 0; d < decals.size(); ++d) { CDecal *decal = decals[d]; - // Get this decal's precomputed vertices and colors + // Get this decal's precomputed vertices, UVs and colors std::vector &verts = decal->getVertices(false); + const std::vector &uvs = decal->getUVs(); const std::vector &colors = decal->getColors(); if (verts.empty()) continue; CMaterial &mat = decal->getMaterial(); + ITexture *tex = mat.getTexture(0); - // Setup GPU texture coordinate generation for this decal, - // matching the legacy non-VP rendering path exactly. - // The GPU generates UVs from vertex positions in object space - // (which equals world space since modelMatrix is Identity), - // then transforms them by the worldToUV matrix. - const CMatrix &worldToUVMatrix = decal->getWorldToUVMatrix(); - mat.enableUserTexMat(0, true); - mat.setTexCoordGen(0, true); - mat.setTexCoordGenMode(0, CMaterial::TexCoordGenObjectSpace); - mat.setUserTexMat(0, worldToUVMatrix); + // Flush on texture change + if (tex != curTex && vbOffset > 0) + { + nlassert(vbOffset % 3 == 0); + drv->renderRawTriangles(decals[d > 0 ? d - 1 : d]->getMaterial(), 0, vbOffset / 3); + vbOffset = 0; + } + curTex = tex; uint32 length = (uint32)verts.size(); - uint32 offset = 0; + uint32 srcOffset = 0; + bool hasUVs = (uvs.size() == verts.size()); bool hasColors = (colors.size() == verts.size()); - while (offset < length) + while (srcOffset < length) { - uint32 remaining = length - offset; - uint32 space = NL3D_DECAL_VB_MAX_VERTICES; + uint32 remaining = length - srcOffset; + uint32 space = NL3D_DECAL_VB_MAX_VERTICES - vbOffset; + + // Flush if VB is full + if (space == 0) + { + nlassert(vbOffset % 3 == 0); + drv->renderRawTriangles(mat, 0, vbOffset / 3); + vbOffset = 0; + space = NL3D_DECAL_VB_MAX_VERTICES; + } + uint32 batch = std::min(remaining, space); { @@ -253,19 +273,27 @@ void CDecalManager::flush(CScene *sc) _VB.lock(vba); for (uint32 i = 0; i < batch; ++i) { - *vba.getVertexCoordPointer(i) = verts[offset + i]; - vba.setColor(i, hasColors ? colors[offset + i] : CRGBA::White); + uint32 vi = vbOffset + i; + uint32 si = srcOffset + i; + *vba.getVertexCoordPointer(vi) = verts[si]; + vba.setTexCoord(vi, 0, hasUVs ? uvs[si] : CUV(0, 0)); + vba.setColor(vi, hasColors ? colors[si] : CRGBA::White); } } - nlassert(batch % 3 == 0); - drv->renderRawTriangles(mat, 0, batch / 3); - offset += batch; + vbOffset += batch; + srcOffset += batch; } + } - // Reset tex coord gen state on the material after rendering - mat.setTexCoordGen(0, false); - mat.enableUserTexMat(0, false); + // Flush remaining vertices for this material group + if (vbOffset > 0) + { + nlassert(vbOffset % 3 == 0); + // Use the last decal's material (they all share the same texture in a group) + CMaterial &lastMat = decals.back()->getMaterial(); + drv->renderRawTriangles(lastMat, 0, vbOffset / 3); + vbOffset = 0; } } } From d28bfcc79d8ec8ee4c0eab00adbcc3f416d17499 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 18 Feb 2026 16:20:47 +0000 Subject: [PATCH 24/24] Fix batch material tracking: use batchMat pointer instead of index arithmetic Co-authored-by: kaetemi <1581053+kaetemi@users.noreply.github.com> --- nel/src/3d/decal_manager.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/nel/src/3d/decal_manager.cpp b/nel/src/3d/decal_manager.cpp index 9826205e26..274ab56aec 100644 --- a/nel/src/3d/decal_manager.cpp +++ b/nel/src/3d/decal_manager.cpp @@ -219,8 +219,9 @@ void CDecalManager::flush(CScene *sc) // Sort decals by priority within this material group (lower priority first) std::sort(decals.begin(), decals.end(), decalPriorityCompare); - // Track current texture for batching: flush only on texture change or VB overflow + // Track current texture and material for batching ITexture *curTex = NULL; + CMaterial *batchMat = NULL; uint32 vbOffset = 0; for (uint d = 0; d < decals.size(); ++d) @@ -242,10 +243,11 @@ void CDecalManager::flush(CScene *sc) if (tex != curTex && vbOffset > 0) { nlassert(vbOffset % 3 == 0); - drv->renderRawTriangles(decals[d > 0 ? d - 1 : d]->getMaterial(), 0, vbOffset / 3); + drv->renderRawTriangles(*batchMat, 0, vbOffset / 3); vbOffset = 0; } curTex = tex; + batchMat = &mat; uint32 length = (uint32)verts.size(); uint32 srcOffset = 0; @@ -287,12 +289,10 @@ void CDecalManager::flush(CScene *sc) } // Flush remaining vertices for this material group - if (vbOffset > 0) + if (vbOffset > 0 && batchMat) { nlassert(vbOffset % 3 == 0); - // Use the last decal's material (they all share the same texture in a group) - CMaterial &lastMat = decals.back()->getMaterial(); - drv->renderRawTriangles(lastMat, 0, vbOffset / 3); + drv->renderRawTriangles(*batchMat, 0, vbOffset / 3); vbOffset = 0; } }