progress on TexturePack and Model serialization, added tinydir as included dependency
This commit is contained in:
parent
1a515f92ec
commit
9269f345b0
@ -2,6 +2,8 @@ The SparrowRenderer has mostly been written by Anselme FRANÇOIS.
|
||||
|
||||
Thanks to LodePNG for texture serialization : http://lodev.org/lodepng
|
||||
|
||||
Thanks to tinydir for directory navigation : https://github.com/cxong/tinydir
|
||||
|
||||
Thanks to GL3W for OpenGL 3.3 core function loading : https://github.com/skaslev/gl3w
|
||||
|
||||
Thanks to Dimitri RAGUET for his little fixes, explanations, and optimisations.
|
150
src/image.cpp
150
src/image.cpp
@ -1,6 +1,8 @@
|
||||
#include "image.h"
|
||||
#include <glm/gtc/noise.hpp>
|
||||
#include "lodepng.h"
|
||||
#include "tinydir.h"
|
||||
#include "texture.h"
|
||||
|
||||
std::string getNameFromFilename(const std::string &filename)
|
||||
{
|
||||
@ -119,18 +121,146 @@ bool Image::save(std::vector<unsigned char> &out)
|
||||
return error != 0;
|
||||
}
|
||||
|
||||
TexturePack::TexturePack(const std::string &filename)
|
||||
void TexturePack::addImage(Image* img)
|
||||
{
|
||||
m_name = getNameFromFilename(filename);
|
||||
std::FILE *file = std::fopen(filename.c_str(), "r");
|
||||
if(file == NULL)
|
||||
return;
|
||||
TexImage texImg;
|
||||
texImg.name = img->m_name;
|
||||
texImg.img = img;
|
||||
texImg.tex = NULL;
|
||||
if(m_images.count(img->m_name))
|
||||
removeImage(img->m_name);
|
||||
m_images[img->m_name] = texImg;
|
||||
}
|
||||
|
||||
bool TexturePack::save(const std::string &path)
|
||||
void TexturePack::removeImage(const std::string &name)
|
||||
{
|
||||
std::string filename = path + '/' + m_name + ".tpk";
|
||||
std::FILE *file = std::fopen(filename.c_str(), "w");
|
||||
if(file == NULL)
|
||||
return false;
|
||||
TexImage &texImg = m_images[name];
|
||||
if(texImg.img != NULL)
|
||||
delete texImg.img;
|
||||
if(texImg.tex != NULL)
|
||||
delete texImg.tex;
|
||||
m_images.erase(name);
|
||||
}
|
||||
|
||||
void TexturePack::addImage(Texture* tex, const std::string &name, bool downloadFromGL)
|
||||
{
|
||||
TexImage texImg;
|
||||
texImg.name = name;
|
||||
texImg.tex = tex;
|
||||
if(downloadFromGL)
|
||||
{
|
||||
texImg.img = tex->getData();
|
||||
texImg.img->m_name = name;
|
||||
}
|
||||
else
|
||||
texImg.img = NULL;
|
||||
if(m_images.count(name))
|
||||
removeImage(name);
|
||||
m_images[name] = texImg;
|
||||
}
|
||||
|
||||
void TexturePack::initGL()
|
||||
{
|
||||
for(std::pair<std::string, TexImage> pair : m_images)
|
||||
{
|
||||
TexImage &texImg = pair.second;
|
||||
if(texImg.img != NULL)
|
||||
{
|
||||
if(texImg.tex != NULL)
|
||||
delete texImg.tex;
|
||||
texImg.tex = new Texture(texImg.img);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TexturePack::initDestroyImages()
|
||||
{
|
||||
for(std::pair<std::string, TexImage> pair : m_images)
|
||||
{
|
||||
TexImage &texImg = pair.second;
|
||||
if(texImg.img != NULL)
|
||||
{
|
||||
if(texImg.tex != NULL)
|
||||
delete texImg.tex;
|
||||
texImg.tex = new Texture(texImg.img);
|
||||
delete texImg.img;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TexturePack::getImagesFromGL()
|
||||
{
|
||||
for(std::pair<std::string, TexImage> pair : m_images)
|
||||
{
|
||||
TexImage &texImg = pair.second;
|
||||
if(texImg.tex != NULL)
|
||||
{
|
||||
if(texImg.img != NULL)
|
||||
delete texImg.img;
|
||||
texImg.img = texImg.tex->getData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TexturePack::destroyGL()
|
||||
{
|
||||
for(std::pair<std::string, TexImage> pair : m_images)
|
||||
{
|
||||
TexImage &texImg = pair.second;
|
||||
if(texImg.tex != NULL)
|
||||
delete texImg.tex;
|
||||
}
|
||||
}
|
||||
|
||||
TexturePack::TexturePack(const std::string &folderPath) : TexturePack()
|
||||
{
|
||||
m_name = getNameFromFilename(folderPath);
|
||||
tinydir_dir dir;
|
||||
int err = tinydir_open(&dir, folderPath.c_str());
|
||||
if(err == -1)
|
||||
{
|
||||
fprintf(stderr, "can't open directory : \"%s\"\n", folderPath.c_str());
|
||||
return;
|
||||
}
|
||||
int n = dir.n_files;
|
||||
for(int i=0; i<n; ++i)
|
||||
{
|
||||
const tinydir_file &file = dir._files[i];
|
||||
if(std::string(file.extension) == "png")
|
||||
{
|
||||
Image* img = new Image(std::string(file.path));
|
||||
img->m_name = file.name;
|
||||
addImage(img);
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "%s is not a png file, ignoring\n", file.path);
|
||||
}
|
||||
}
|
||||
|
||||
bool TexturePack::save(const std::string &folderPath)
|
||||
{
|
||||
std::string path = folderPath + '/' + m_name;
|
||||
if(!tinydir_create_directory(path.c_str()))
|
||||
{
|
||||
fprintf(stderr, "can't create directory \"%s\"\n", path.c_str());
|
||||
return false;
|
||||
}
|
||||
tinydir_dir dir;
|
||||
int err = tinydir_open(&dir, path.c_str());
|
||||
if(err == -1)
|
||||
{
|
||||
fprintf(stderr, "can't open directory : \"%s\"\n", path.c_str());
|
||||
return false;
|
||||
}
|
||||
// directory is open
|
||||
for(std::pair<std::string, TexImage> pair : m_images)
|
||||
{
|
||||
TexImage &texImg = pair.second;
|
||||
if(texImg.img != NULL)
|
||||
{
|
||||
std::string filename = path + '/' + texImg.name + ".png";
|
||||
if(!texImg.img->save(filename))
|
||||
fprintf(stderr, "can't save image : \"%s\"\n", filename.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
src/image.h
10
src/image.h
@ -42,10 +42,12 @@ private:
|
||||
std::string name;
|
||||
Texture* tex;
|
||||
Image* img;
|
||||
|
||||
TexImage() : tex(NULL), img(NULL) {}
|
||||
};
|
||||
|
||||
std::string m_name;
|
||||
std::unordered_map<std::string, Image> m_images;
|
||||
std::unordered_map<std::string, TexImage> m_images;
|
||||
bool allocatedImg;
|
||||
bool allocatedTex;
|
||||
|
||||
@ -69,10 +71,16 @@ public:
|
||||
* @brief addImage adds an image to the pack, if an image of this name already exists in the pack, it will be replaced.
|
||||
*/
|
||||
void addImage(Image* img);
|
||||
|
||||
/**
|
||||
* @brief removeImage removes the image named "name" from the pack if it exists.
|
||||
*/
|
||||
void removeImage(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief addImage adds an image from an OpenGL texture and names it with the provided name.
|
||||
* @param downloadFromGL if this is false, the Image will not be downloaded from the texture, only the texture will be added.
|
||||
* this does not support cubemap or array textures (only the first face will be added)
|
||||
*/
|
||||
void addImage(Texture* tex, const std::string &name, bool downloadFromGL = true);
|
||||
|
||||
|
86
src/mesh.cpp
86
src/mesh.cpp
@ -39,6 +39,7 @@ Mesh::Mesh(const std::string &name) :
|
||||
isBillboard(false),
|
||||
isShadowCaster(true),
|
||||
depth(0),
|
||||
m_flags(0),
|
||||
primitive_type(GL_TRIANGLES),
|
||||
vao(0)
|
||||
{
|
||||
@ -65,8 +66,39 @@ void Mesh::clearBuffers()
|
||||
buffers.clear();
|
||||
}
|
||||
|
||||
unsigned int Mesh::updateFlags()
|
||||
{
|
||||
m_flags = 0;
|
||||
|
||||
if(!indices.empty())
|
||||
m_flags |= 1 << MESH_INDEXED;
|
||||
if(!texCoords.empty())
|
||||
m_flags |= 1 << MESH_TEXTURABLE;
|
||||
if(!instances_offsets.empty())
|
||||
m_flags |= 1 << MESH_INSTANCED;
|
||||
|
||||
if(!positions3D.empty())
|
||||
{
|
||||
m_flags |= 1 << MESH_3D;
|
||||
|
||||
if(!tangents.empty())
|
||||
m_flags |= 1 << MESH_TANGENT_SPACE;
|
||||
if(isDoubleSided)
|
||||
m_flags |= 1 << MESH_DOUBLE_SIDED;
|
||||
if(isBillboard)
|
||||
m_flags |= 1 << MESH_BILLBOARD;
|
||||
if(isShadowCaster)
|
||||
m_flags |= 1 << MESH_SHADOWED;
|
||||
}
|
||||
else
|
||||
m_flags |= 1 << MESH_2D;
|
||||
|
||||
return m_flags | material->getFlags();
|
||||
}
|
||||
|
||||
void Mesh::initGL()
|
||||
{
|
||||
updateFlags();
|
||||
destroyGL();
|
||||
|
||||
// create VAO
|
||||
@ -197,32 +229,7 @@ void Mesh::clearData()
|
||||
|
||||
unsigned int Mesh::getFlags()
|
||||
{
|
||||
unsigned int flags = material->getFlags();
|
||||
|
||||
if(!(indices.empty() || buffersId[INDICES_BUFFER] == -1))
|
||||
flags |= 1 << MESH_INDEXED;
|
||||
if(!(texCoords.empty() || buffersId[TEXCOORD_BUFFER] == -1))
|
||||
flags |= 1 << MESH_TEXTURABLE;
|
||||
if(!(instances_offsets.empty() || buffersId[INSTANCE_BUFFER] == -1))
|
||||
flags |= 1 << MESH_INSTANCED;
|
||||
|
||||
if(!(positions3D.empty() || buffersId[POSITION3D_BUFFER] == -1))
|
||||
{
|
||||
flags |= 1 << MESH_3D;
|
||||
|
||||
if((!tangents.empty() || buffersId[TANGENT_BUFFER] == -1))
|
||||
flags |= 1 << MESH_TANGENT_SPACE;
|
||||
if(isDoubleSided)
|
||||
flags |= 1 << MESH_DOUBLE_SIDED;
|
||||
if(isBillboard)
|
||||
flags |= 1 << MESH_BILLBOARD;
|
||||
if(isShadowCaster)
|
||||
flags |= 1 << MESH_SHADOWED;
|
||||
}
|
||||
else
|
||||
flags |= 1 << MESH_2D;
|
||||
|
||||
return flags;
|
||||
return m_flags | material->getFlags();
|
||||
}
|
||||
|
||||
void Mesh::addRectangle2D(const glm::vec2 &pos, const glm::vec2 &dim, const glm::vec2 &texCoord, const glm::vec2 &texRange, bool indexed)
|
||||
@ -249,6 +256,33 @@ void Mesh::addRectangle2D(const glm::vec2 &pos, const glm::vec2 &dim, const glm:
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::setIsDoubleSided(bool val)
|
||||
{
|
||||
isDoubleSided = val;
|
||||
if(val)
|
||||
m_flags |= 1 << MESH_DOUBLE_SIDED;
|
||||
else
|
||||
m_flags &= ~(1 << MESH_DOUBLE_SIDED);
|
||||
}
|
||||
|
||||
void Mesh::setIsBillboard(bool val)
|
||||
{
|
||||
isBillboard = val;
|
||||
if(val)
|
||||
m_flags |= 1 << MESH_BILLBOARD;
|
||||
else
|
||||
m_flags &= ~(1 << MESH_BILLBOARD);
|
||||
}
|
||||
|
||||
void Mesh::setIsShadowCaster(bool val)
|
||||
{
|
||||
isShadowCaster = val;
|
||||
if(val)
|
||||
m_flags |= 1 << MESH_SHADOWED;
|
||||
else
|
||||
m_flags &= ~(1 << MESH_SHADOWED);
|
||||
}
|
||||
|
||||
struct VertexComparator
|
||||
{
|
||||
// c'est plutot crade mais j'ai pas trouve d'autre moyen pour le moment
|
||||
|
21
src/mesh.h
21
src/mesh.h
@ -109,7 +109,8 @@ public:
|
||||
void clearData();
|
||||
|
||||
/**
|
||||
* @brief getFlags returns the flags that defines the specificities of the mesh and his material
|
||||
* @brief getFlags returns the flags that defines the specificities of the mesh and his material.
|
||||
* @warning the flags are correct only if initGL has been called.
|
||||
*/
|
||||
unsigned int getFlags();
|
||||
|
||||
@ -152,7 +153,7 @@ public:
|
||||
* @brief setDepth allows to set the depth of a 2D mesh, the depth must be between -1 and 1, -1 being the closest to the camera
|
||||
*/
|
||||
void setDepth(float d) { depth = d; }
|
||||
float getDepth() { return depth; } const
|
||||
float getDepth() const { return depth; }
|
||||
|
||||
/*************************************************************/
|
||||
/* 3D MESH PROPERTIES */
|
||||
@ -161,19 +162,19 @@ public:
|
||||
/**
|
||||
* @brief setIsDoubleSided allows to enable or disable face culling for this Mesh
|
||||
*/
|
||||
void setIsDoubleSided(bool val) {isDoubleSided = val;}
|
||||
void setIsDoubleSided(bool val);
|
||||
|
||||
/**
|
||||
* @brief setIsBillboard allows to enable or disable billboarding,
|
||||
* a billboard mesh will always follow the camera orientation
|
||||
*/
|
||||
void setIsBillboard(bool val) {isBillboard = val;}
|
||||
void setIsBillboard(bool val);
|
||||
|
||||
/**
|
||||
* @brief setIsShadowCaster allows to enable or disable,
|
||||
* rendering of this mesh in the shadowmaps
|
||||
*/
|
||||
void setIsShadowCaster(bool val) {isShadowCaster = val;}
|
||||
void setIsShadowCaster(bool val);
|
||||
|
||||
/*************************************************************/
|
||||
/* 3D MESH TOOLS */
|
||||
@ -234,7 +235,14 @@ public:
|
||||
* default is GL_TRIANGLES
|
||||
*/
|
||||
void setPrimitiveType(GLenum type) {primitive_type = type;}
|
||||
|
||||
|
||||
/**
|
||||
* @brief updateFlags forces the refresh of the mesh flags,
|
||||
* they will be computed from the CPU-side buffers status
|
||||
* (so don't call it after a call to clearData)
|
||||
*/
|
||||
unsigned int updateFlags();
|
||||
|
||||
protected:
|
||||
std::string m_name;
|
||||
|
||||
@ -243,6 +251,7 @@ protected:
|
||||
bool isBillboard;
|
||||
bool isShadowCaster;
|
||||
float depth;
|
||||
unsigned int m_flags;
|
||||
|
||||
GLenum primitive_type;
|
||||
|
||||
|
@ -12,62 +12,32 @@ Model::~Model()
|
||||
destroyGL();
|
||||
for(Mesh *m : m_meshes)
|
||||
delete m;
|
||||
}
|
||||
/*
|
||||
Image* Model::getImage(const std::string &name)
|
||||
{
|
||||
for(TextureImg &tex : m_textures)
|
||||
{
|
||||
if(name.compare(tex.name) == 0)
|
||||
return tex.img;
|
||||
}
|
||||
return NULL;
|
||||
setTexturePack(NULL);
|
||||
}
|
||||
|
||||
Texture* Model::getTexture(const std::string &name)
|
||||
void Model::setTexturePack(TexturePack *texPack)
|
||||
{
|
||||
for(TextureImg &tex : m_textures)
|
||||
{
|
||||
if(name.compare(tex.name) == 0)
|
||||
return tex.tex;
|
||||
}
|
||||
return NULL;
|
||||
}*/
|
||||
if(m_texPack != NULL)
|
||||
delete m_texPack;
|
||||
m_texPack = texPack;
|
||||
}
|
||||
|
||||
void Model::destroy()
|
||||
{
|
||||
for(Mesh* m : m_meshes)
|
||||
m->clearData();
|
||||
for(TextureImg &tex : m_textures)
|
||||
{
|
||||
delete tex.img;
|
||||
tex.img = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void Model::initGL()
|
||||
{
|
||||
for(Mesh* m : m_meshes)
|
||||
m->initGL();
|
||||
for(TextureImg &tex : m_textures)
|
||||
{
|
||||
if(tex.tex == NULL)
|
||||
tex.tex = new Texture(tex.img);
|
||||
}
|
||||
}
|
||||
|
||||
void Model::destroyGL()
|
||||
{
|
||||
for(Mesh* m : m_meshes)
|
||||
m->destroyGL();
|
||||
for(TextureImg &tex : m_textures)
|
||||
{
|
||||
if(tex.tex != NULL)
|
||||
{
|
||||
delete tex.tex;
|
||||
tex.tex = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Model::save(const std::string &filename, const std::string &texturesPath)
|
||||
|
22
src/model.h
22
src/model.h
@ -11,39 +11,21 @@ class TexturePack;
|
||||
class Model
|
||||
{
|
||||
protected:
|
||||
/*struct TextureImg
|
||||
{
|
||||
std::string name;
|
||||
Texture* tex;
|
||||
Image* img;
|
||||
|
||||
TextureImg(const std::string n, Image* i) :
|
||||
name(n), tex(NULL), img(i) {}
|
||||
};*/
|
||||
|
||||
TexturePack *m_texPack;
|
||||
std::vector<Mesh*> m_meshes;
|
||||
|
||||
//std::vector<TextureImg> m_textures;
|
||||
|
||||
public:
|
||||
Model();
|
||||
|
||||
~Model();
|
||||
|
||||
void setTexturePack(TexturePack *texPack);
|
||||
|
||||
/**
|
||||
* @brief addMesh adds a mesh to the mesh list
|
||||
*/
|
||||
void addMesh(Mesh *mesh) { m_meshes.push_back(mesh); }
|
||||
|
||||
/**
|
||||
* @brief addImage adds an image to the texture pack
|
||||
*/
|
||||
/*void addImage(const std::string &name, Image* img)
|
||||
{
|
||||
m_textures.push_back(TextureImg(name, img));
|
||||
}*/
|
||||
|
||||
/**
|
||||
* @brief getMeshes returns the mesh array
|
||||
*/
|
||||
|
793
src/tinydir.h
Normal file
793
src/tinydir.h
Normal file
@ -0,0 +1,793 @@
|
||||
/*
|
||||
Copyright (c) 2013-2016, tinydir authors:
|
||||
- Cong Xu
|
||||
- Lautis Sun
|
||||
- Baudouin Feildel
|
||||
- Andargor <andargor@yahoo.com>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
this file has been slightly modified by EpicSparrow to add the tinydir_create_directory function
|
||||
*/
|
||||
|
||||
#ifndef TINYDIR_H
|
||||
#define TINYDIR_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if ((defined _UNICODE) && !(defined UNICODE))
|
||||
#define UNICODE
|
||||
#endif
|
||||
|
||||
#if ((defined UNICODE) && !(defined _UNICODE))
|
||||
#define _UNICODE
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef _MSC_VER
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
# include <tchar.h>
|
||||
# pragma warning(push)
|
||||
# pragma warning (disable : 4996)
|
||||
#else
|
||||
# include <dirent.h>
|
||||
# include <libgen.h>
|
||||
# include <sys/stat.h>
|
||||
# include <stddef.h>
|
||||
#endif
|
||||
#ifdef __MINGW32__
|
||||
# include <direct.h>
|
||||
# include <tchar.h>
|
||||
#endif
|
||||
|
||||
bool tinydir_create_directory(const char* path)
|
||||
{
|
||||
|
||||
int nError = 0;
|
||||
#if defined(_WIN32)
|
||||
nError = _mkdir(path); // can be used on Windows
|
||||
#else
|
||||
mode_t nMode = 0733; // UNIX style permissions
|
||||
nError = mkdir(path, nMode); // can be used on non-Windows
|
||||
#endif
|
||||
return nError == 0;
|
||||
}
|
||||
|
||||
/* types */
|
||||
|
||||
/* Windows UNICODE wide character support */
|
||||
#if defined _MSC_VER || defined __MINGW32__
|
||||
#define _tinydir_char_t TCHAR
|
||||
#define TINYDIR_STRING(s) _TEXT(s)
|
||||
#define _tinydir_strlen _tcslen
|
||||
#define _tinydir_strcpy _tcscpy
|
||||
#define _tinydir_strcat _tcscat
|
||||
#define _tinydir_strcmp _tcscmp
|
||||
#define _tinydir_strrchr _tcsrchr
|
||||
#define _tinydir_strncmp _tcsncmp
|
||||
#else
|
||||
#define _tinydir_char_t char
|
||||
#define TINYDIR_STRING(s) s
|
||||
#define _tinydir_strlen strlen
|
||||
#define _tinydir_strcpy strcpy
|
||||
#define _tinydir_strcat strcat
|
||||
#define _tinydir_strcmp strcmp
|
||||
#define _tinydir_strrchr strrchr
|
||||
#define _tinydir_strncmp strncmp
|
||||
#endif
|
||||
|
||||
#define _TINYDIR_PATH_MAX 4096
|
||||
#ifdef _MSC_VER
|
||||
/* extra chars for the "\\*" mask */
|
||||
# define _TINYDIR_PATH_EXTRA 2
|
||||
#else
|
||||
# define _TINYDIR_PATH_EXTRA 0
|
||||
#endif
|
||||
#define _TINYDIR_FILENAME_MAX 256
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define _TINYDIR_FUNC static __inline
|
||||
#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
|
||||
# define _TINYDIR_FUNC static __inline__
|
||||
#else
|
||||
# define _TINYDIR_FUNC static inline
|
||||
#endif
|
||||
|
||||
/* readdir_r is a POSIX-only function, and may not be available under various
|
||||
* environments/settings, e.g. MinGW. Use readdir fallback */
|
||||
#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\
|
||||
_POSIX_SOURCE
|
||||
# define _TINYDIR_HAS_READDIR_R
|
||||
#endif
|
||||
#if _POSIX_C_SOURCE >= 200112L
|
||||
# define _TINYDIR_HAS_FPATHCONF
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#if _BSD_SOURCE || _SVID_SOURCE || \
|
||||
(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
|
||||
# define _TINYDIR_HAS_DIRFD
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\
|
||||
defined _PC_NAME_MAX
|
||||
# define _TINYDIR_USE_FPATHCONF
|
||||
#endif
|
||||
#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\
|
||||
!(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX)
|
||||
# define _TINYDIR_USE_READDIR
|
||||
#endif
|
||||
|
||||
/* MINGW32 has two versions of dirent, ASCII and UNICODE*/
|
||||
#ifndef _MSC_VER
|
||||
#if (defined __MINGW32__) && (defined _UNICODE)
|
||||
#define _TINYDIR_DIR _WDIR
|
||||
#define _tinydir_dirent _wdirent
|
||||
#define _tinydir_opendir _wopendir
|
||||
#define _tinydir_readdir _wreaddir
|
||||
#define _tinydir_closedir _wclosedir
|
||||
#else
|
||||
#define _TINYDIR_DIR DIR
|
||||
#define _tinydir_dirent dirent
|
||||
#define _tinydir_opendir opendir
|
||||
#define _tinydir_readdir readdir
|
||||
#define _tinydir_closedir closedir
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */
|
||||
#if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE)
|
||||
#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE)
|
||||
#else
|
||||
#error "Either define both alloc and free or none of them!"
|
||||
#endif
|
||||
|
||||
#if !defined(_TINYDIR_MALLOC)
|
||||
#define _TINYDIR_MALLOC(_size) malloc(_size)
|
||||
#define _TINYDIR_FREE(_ptr) free(_ptr)
|
||||
#endif /* !defined(_TINYDIR_MALLOC) */
|
||||
|
||||
typedef struct tinydir_file
|
||||
{
|
||||
_tinydir_char_t path[_TINYDIR_PATH_MAX];
|
||||
_tinydir_char_t name[_TINYDIR_FILENAME_MAX];
|
||||
_tinydir_char_t *extension;
|
||||
int is_dir;
|
||||
int is_reg;
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#ifdef __MINGW32__
|
||||
struct _stat _s;
|
||||
#else
|
||||
struct stat _s;
|
||||
#endif
|
||||
#endif
|
||||
} tinydir_file;
|
||||
|
||||
typedef struct tinydir_dir
|
||||
{
|
||||
_tinydir_char_t path[_TINYDIR_PATH_MAX];
|
||||
int has_next;
|
||||
size_t n_files;
|
||||
|
||||
tinydir_file *_files;
|
||||
#ifdef _MSC_VER
|
||||
HANDLE _h;
|
||||
WIN32_FIND_DATA _f;
|
||||
#else
|
||||
_TINYDIR_DIR *_d;
|
||||
struct _tinydir_dirent *_e;
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
struct _tinydir_dirent *_ep;
|
||||
#endif
|
||||
#endif
|
||||
} tinydir_dir;
|
||||
|
||||
|
||||
/* declarations */
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path);
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path);
|
||||
_TINYDIR_FUNC
|
||||
void tinydir_close(tinydir_dir *dir);
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_next(tinydir_dir *dir);
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
|
||||
|
||||
_TINYDIR_FUNC
|
||||
void _tinydir_get_ext(tinydir_file *file);
|
||||
_TINYDIR_FUNC
|
||||
int _tinydir_file_cmp(const void *a, const void *b);
|
||||
#ifndef _MSC_VER
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
_TINYDIR_FUNC
|
||||
size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* definitions*/
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
|
||||
{
|
||||
#ifndef _MSC_VER
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
int error;
|
||||
int size; /* using int size */
|
||||
#endif
|
||||
#else
|
||||
_tinydir_char_t path_buf[_TINYDIR_PATH_MAX];
|
||||
#endif
|
||||
_tinydir_char_t *pathp;
|
||||
|
||||
if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* initialise dir */
|
||||
dir->_files = NULL;
|
||||
#ifdef _MSC_VER
|
||||
dir->_h = INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
dir->_d = NULL;
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
dir->_ep = NULL;
|
||||
#endif
|
||||
#endif
|
||||
tinydir_close(dir);
|
||||
|
||||
_tinydir_strcpy(dir->path, path);
|
||||
/* Remove trailing slashes */
|
||||
pathp = &dir->path[_tinydir_strlen(dir->path) - 1];
|
||||
while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/')))
|
||||
{
|
||||
*pathp = TINYDIR_STRING('\0');
|
||||
pathp++;
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
_tinydir_strcpy(path_buf, dir->path);
|
||||
_tinydir_strcat(path_buf, TINYDIR_STRING("\\*"));
|
||||
dir->_h = FindFirstFile(path_buf, &dir->_f);
|
||||
if (dir->_h == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
errno = ENOENT;
|
||||
#else
|
||||
dir->_d = _tinydir_opendir(path);
|
||||
if (dir->_d == NULL)
|
||||
{
|
||||
#endif
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* read first file */
|
||||
dir->has_next = 1;
|
||||
#ifndef _MSC_VER
|
||||
#ifdef _TINYDIR_USE_READDIR
|
||||
dir->_e = _tinydir_readdir(dir->_d);
|
||||
#else
|
||||
/* allocate dirent buffer for readdir_r */
|
||||
size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */
|
||||
if (size == -1) return -1;
|
||||
dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size);
|
||||
if (dir->_ep == NULL) return -1;
|
||||
|
||||
error = readdir_r(dir->_d, dir->_ep, &dir->_e);
|
||||
if (error != 0) return -1;
|
||||
#endif
|
||||
if (dir->_e == NULL)
|
||||
{
|
||||
dir->has_next = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
tinydir_close(dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
|
||||
{
|
||||
/* Count the number of files first, to pre-allocate the files array */
|
||||
size_t n_files = 0;
|
||||
if (tinydir_open(dir, path) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
while (dir->has_next)
|
||||
{
|
||||
n_files++;
|
||||
if (tinydir_next(dir) == -1)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
}
|
||||
tinydir_close(dir);
|
||||
|
||||
if (tinydir_open(dir, path) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
dir->n_files = 0;
|
||||
dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
|
||||
if (dir->_files == NULL)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
while (dir->has_next)
|
||||
{
|
||||
tinydir_file *p_file;
|
||||
dir->n_files++;
|
||||
|
||||
p_file = &dir->_files[dir->n_files - 1];
|
||||
if (tinydir_readfile(dir, p_file) == -1)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (tinydir_next(dir) == -1)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/* Just in case the number of files has changed between the first and
|
||||
second reads, terminate without writing into unallocated memory */
|
||||
if (dir->n_files == n_files)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
|
||||
|
||||
return 0;
|
||||
|
||||
bail:
|
||||
tinydir_close(dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
void tinydir_close(tinydir_dir *dir)
|
||||
{
|
||||
if (dir == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
memset(dir->path, 0, sizeof(dir->path));
|
||||
dir->has_next = 0;
|
||||
dir->n_files = 0;
|
||||
_TINYDIR_FREE(dir->_files);
|
||||
dir->_files = NULL;
|
||||
#ifdef _MSC_VER
|
||||
if (dir->_h != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FindClose(dir->_h);
|
||||
}
|
||||
dir->_h = INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
if (dir->_d)
|
||||
{
|
||||
_tinydir_closedir(dir->_d);
|
||||
}
|
||||
dir->_d = NULL;
|
||||
dir->_e = NULL;
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
_TINYDIR_FREE(dir->_ep);
|
||||
dir->_ep = NULL;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_next(tinydir_dir *dir)
|
||||
{
|
||||
if (dir == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (!dir->has_next)
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
if (FindNextFile(dir->_h, &dir->_f) == 0)
|
||||
#else
|
||||
#ifdef _TINYDIR_USE_READDIR
|
||||
dir->_e = _tinydir_readdir(dir->_d);
|
||||
#else
|
||||
if (dir->_ep == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
if (dir->_e == NULL)
|
||||
#endif
|
||||
{
|
||||
dir->has_next = 0;
|
||||
#ifdef _MSC_VER
|
||||
if (GetLastError() != ERROR_SUCCESS &&
|
||||
GetLastError() != ERROR_NO_MORE_FILES)
|
||||
{
|
||||
tinydir_close(dir);
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
|
||||
{
|
||||
if (dir == NULL || file == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
if (dir->_h == INVALID_HANDLE_VALUE)
|
||||
#else
|
||||
if (dir->_e == NULL)
|
||||
#endif
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
if (_tinydir_strlen(dir->path) +
|
||||
_tinydir_strlen(
|
||||
#ifdef _MSC_VER
|
||||
dir->_f.cFileName
|
||||
#else
|
||||
dir->_e->d_name
|
||||
#endif
|
||||
) + 1 + _TINYDIR_PATH_EXTRA >=
|
||||
_TINYDIR_PATH_MAX)
|
||||
{
|
||||
/* the path for the file will be too long */
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
if (_tinydir_strlen(
|
||||
#ifdef _MSC_VER
|
||||
dir->_f.cFileName
|
||||
#else
|
||||
dir->_e->d_name
|
||||
#endif
|
||||
) >= _TINYDIR_FILENAME_MAX)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_tinydir_strcpy(file->path, dir->path);
|
||||
_tinydir_strcat(file->path, TINYDIR_STRING("/"));
|
||||
_tinydir_strcpy(file->name,
|
||||
#ifdef _MSC_VER
|
||||
dir->_f.cFileName
|
||||
#else
|
||||
dir->_e->d_name
|
||||
#endif
|
||||
);
|
||||
_tinydir_strcat(file->path, file->name);
|
||||
#ifndef _MSC_VER
|
||||
#ifdef __MINGW32__
|
||||
if (_tstat(
|
||||
#else
|
||||
if (stat(
|
||||
#endif
|
||||
file->path, &file->_s) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
_tinydir_get_ext(file);
|
||||
|
||||
file->is_dir =
|
||||
#ifdef _MSC_VER
|
||||
!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||
#else
|
||||
S_ISDIR(file->_s.st_mode);
|
||||
#endif
|
||||
file->is_reg =
|
||||
#ifdef _MSC_VER
|
||||
!!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
|
||||
(
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
|
||||
#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
|
||||
#endif
|
||||
#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
|
||||
#endif
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
|
||||
!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
|
||||
#else
|
||||
S_ISREG(file->_s.st_mode);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
|
||||
{
|
||||
if (dir == NULL || file == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (i >= dir->n_files)
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(file, &dir->_files[i], sizeof(tinydir_file));
|
||||
_tinydir_get_ext(file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
|
||||
{
|
||||
_tinydir_char_t path[_TINYDIR_PATH_MAX];
|
||||
if (dir == NULL)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (i >= dir->n_files || !dir->_files[i].is_dir)
|
||||
{
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
_tinydir_strcpy(path, dir->_files[i].path);
|
||||
tinydir_close(dir);
|
||||
if (tinydir_open_sorted(dir, path) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Open a single file given its path */
|
||||
_TINYDIR_FUNC
|
||||
int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
|
||||
{
|
||||
tinydir_dir dir;
|
||||
int result = 0;
|
||||
int found = 0;
|
||||
_tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX];
|
||||
_tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX];
|
||||
_tinydir_char_t *dir_name;
|
||||
_tinydir_char_t *base_name;
|
||||
#if (defined _MSC_VER || defined __MINGW32__)
|
||||
_tinydir_char_t drive_buf[_TINYDIR_PATH_MAX];
|
||||
_tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
|
||||
#endif
|
||||
|
||||
if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get the parent path */
|
||||
#if (defined _MSC_VER || defined __MINGW32__)
|
||||
#if ((defined _MSC_VER) && (_MSC_VER >= 1400))
|
||||
_tsplitpath_s(
|
||||
path,
|
||||
drive_buf, sizeof drive_buf,
|
||||
dir_name_buf, sizeof dir_name_buf,
|
||||
file_name_buf, sizeof file_name_buf,
|
||||
ext_buf, sizeof ext_buf);
|
||||
#else
|
||||
_tsplitpath(
|
||||
path,
|
||||
drive_buf,
|
||||
dir_name_buf,
|
||||
file_name_buf,
|
||||
ext_buf);
|
||||
#endif
|
||||
if (errno)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
/* Emulate the behavior of dirname by returning "." for dir name if it's
|
||||
empty */
|
||||
#if ((defined _MSC_VER || defined __MINGW32__) && (defined UNICODE))
|
||||
if (drive_buf[0] == '\0' && drive_buf[1] == '\0' && dir_name_buf[0] == '\0' && dir_name_buf[1] == '\0')
|
||||
#else
|
||||
if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
|
||||
#endif
|
||||
{
|
||||
_tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
|
||||
}
|
||||
/* Concatenate the drive letter and dir name to form full dir name */
|
||||
_tinydir_strcat(drive_buf, dir_name_buf);
|
||||
dir_name = drive_buf;
|
||||
/* Concatenate the file name and extension to form base name */
|
||||
_tinydir_strcat(file_name_buf, ext_buf);
|
||||
base_name = file_name_buf;
|
||||
#else
|
||||
_tinydir_strcpy(dir_name_buf, path);
|
||||
dir_name = dirname(dir_name_buf);
|
||||
_tinydir_strcpy(file_name_buf, path);
|
||||
base_name =basename(file_name_buf);
|
||||
#endif
|
||||
|
||||
/* Open the parent directory */
|
||||
if (tinydir_open(&dir, dir_name) == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Read through the parent directory and look for the file */
|
||||
while (dir.has_next)
|
||||
{
|
||||
if (tinydir_readfile(&dir, file) == -1)
|
||||
{
|
||||
result = -1;
|
||||
goto bail;
|
||||
}
|
||||
if (_tinydir_strcmp(file->name, base_name) == 0)
|
||||
{
|
||||
/* File found */
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
tinydir_next(&dir);
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
result = -1;
|
||||
errno = ENOENT;
|
||||
}
|
||||
|
||||
bail:
|
||||
tinydir_close(&dir);
|
||||
return result;
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
void _tinydir_get_ext(tinydir_file *file)
|
||||
{
|
||||
_tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
|
||||
if (period == NULL)
|
||||
{
|
||||
file->extension = &(file->name[_tinydir_strlen(file->name)]);
|
||||
}
|
||||
else
|
||||
{
|
||||
file->extension = period + 1;
|
||||
}
|
||||
}
|
||||
|
||||
_TINYDIR_FUNC
|
||||
int _tinydir_file_cmp(const void *a, const void *b)
|
||||
{
|
||||
const tinydir_file *fa = (const tinydir_file *)a;
|
||||
const tinydir_file *fb = (const tinydir_file *)b;
|
||||
if (fa->is_dir != fb->is_dir)
|
||||
{
|
||||
return -(fa->is_dir - fb->is_dir);
|
||||
}
|
||||
return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
|
||||
}
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#ifndef _TINYDIR_USE_READDIR
|
||||
/*
|
||||
The following authored by Ben Hutchings <ben@decadent.org.uk>
|
||||
from https://womble.decadent.org.uk/readdir_r-advisory.html
|
||||
*/
|
||||
/* Calculate the required buffer size (in bytes) for directory *
|
||||
* entries read from the given directory handle. Return -1 if this *
|
||||
* this cannot be done. *
|
||||
* *
|
||||
* This code does not trust values of NAME_MAX that are less than *
|
||||
* 255, since some systems (including at least HP-UX) incorrectly *
|
||||
* define it to be a smaller value. */
|
||||
_TINYDIR_FUNC
|
||||
size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
|
||||
{
|
||||
long name_max;
|
||||
size_t name_end;
|
||||
/* parameter may be unused */
|
||||
(void)dirp;
|
||||
|
||||
#if defined _TINYDIR_USE_FPATHCONF
|
||||
name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
|
||||
if (name_max == -1)
|
||||
#if defined(NAME_MAX)
|
||||
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
|
||||
#else
|
||||
return (size_t)(-1);
|
||||
#endif
|
||||
#elif defined(NAME_MAX)
|
||||
name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
|
||||
#else
|
||||
#error "buffer size for readdir_r cannot be determined"
|
||||
#endif
|
||||
name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1;
|
||||
return (name_end > sizeof(struct _tinydir_dirent) ?
|
||||
name_end : sizeof(struct _tinydir_dirent));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
# if defined (_MSC_VER)
|
||||
# pragma warning(pop)
|
||||
# endif
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user