mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-21 20:56:23 +00:00
Core/Misc: update g3dlite lib (#2904)
* Core/Misc: update g3dlite lib * update Co-authored-by: Francesco Borzì <borzifrancesco@gmail.com>
This commit is contained in:
322
deps/g3dlite/source/fileutils.cpp
vendored
322
deps/g3dlite/source/fileutils.cpp
vendored
@@ -25,7 +25,7 @@
|
||||
#include "zip.h"
|
||||
#endif
|
||||
|
||||
#ifdef G3D_WIN32
|
||||
#ifdef G3D_WINDOWS
|
||||
// Needed for _getcwd
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
@@ -63,7 +63,7 @@ std::string resolveFilename(const std::string& filename) {
|
||||
return filename;
|
||||
} else {
|
||||
|
||||
#ifdef G3D_WIN32
|
||||
#ifdef G3D_WINDOWS
|
||||
if ((filename.size() >= 2) && (filename[1] == ':')) {
|
||||
// There is a drive spec on the front.
|
||||
if ((filename.size() >= 3) && ((filename[2] == '\\') ||
|
||||
@@ -86,36 +86,43 @@ std::string resolveFilename(const std::string& filename) {
|
||||
char buffer[1024];
|
||||
|
||||
// Prepend the working directory.
|
||||
_getcwd(buffer, 1024);
|
||||
(void)_getcwd(buffer, 1024);
|
||||
|
||||
return format("%s/%s", buffer, filename.c_str());
|
||||
}
|
||||
|
||||
bool zipfileExists(const std::string& filename) {
|
||||
std::string outZipfile;
|
||||
std::string outInternalFile;
|
||||
std::string outZipfile;
|
||||
std::string outInternalFile;
|
||||
return zipfileExists(filename, outZipfile, outInternalFile);
|
||||
}
|
||||
|
||||
std::string readWholeFile(
|
||||
const std::string& filename) {
|
||||
|
||||
_internal::currentFilesUsed.insert(filename);
|
||||
std::string readWholeFile(const std::string& filename) {
|
||||
|
||||
std::string s;
|
||||
|
||||
debugAssert(filename != "");
|
||||
debugAssertM(FileSystem::exists(filename), filename + " not found");
|
||||
if (! FileSystem::exists(filename)) {
|
||||
throw (filename + " not found");
|
||||
}
|
||||
FileSystem::markFileUsed(filename);
|
||||
std::string zipfile;
|
||||
|
||||
if (! FileSystem::inZipfile(filename, zipfile)) {
|
||||
// Not in zipfile
|
||||
if (! FileSystem::exists(filename)) {
|
||||
throw FileNotFound(filename, std::string("File not found in readWholeFile: ") + filename);
|
||||
}
|
||||
|
||||
if (! FileSystem::inZipfile(filename)) {
|
||||
int64 length = FileSystem::size(filename);
|
||||
|
||||
char* buffer = (char*)System::alignedMalloc(length + 1, 16);
|
||||
debugAssert(buffer);
|
||||
FILE* f = FileSystem::fopen(filename.c_str(), "rb");
|
||||
debugAssert(f);
|
||||
int ret = fread(buffer, 1, length, f);
|
||||
debugAssert(ret == length);(void)ret;
|
||||
int64 ret = fread(buffer, 1, length, f);
|
||||
debugAssert(ret == length);(void)ret;
|
||||
FileSystem::fclose(f);
|
||||
|
||||
buffer[length] = '\0';
|
||||
@@ -123,60 +130,43 @@ std::string readWholeFile(
|
||||
|
||||
System::alignedFree(buffer);
|
||||
|
||||
} else if (zipfileExists(filename)) {
|
||||
} else {
|
||||
|
||||
void* zipBuffer;
|
||||
size_t length;
|
||||
zipRead(filename, zipBuffer, length);
|
||||
// In zipfile
|
||||
FileSystem::markFileUsed(zipfile);
|
||||
|
||||
char* buffer = (char*)System::alignedMalloc(length + 1, 16);
|
||||
System::memcpy(buffer,zipBuffer, length + 1);
|
||||
zipClose(zipBuffer);
|
||||
|
||||
buffer[length] = '\0';
|
||||
s = std::string(buffer);
|
||||
System::alignedFree(buffer);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
void zipRead(const std::string& file,
|
||||
void*& data,
|
||||
size_t& length) {
|
||||
std::string zip, desiredFile;
|
||||
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
|
||||
if (zipfileExists(file, zip, desiredFile)) {
|
||||
struct zip *z = zip_open( zip.c_str(), ZIP_CHECKCONS, NULL );
|
||||
// Zipfiles require Unix-style slashes
|
||||
std::string internalFile = FilePath::canonicalize(filename.substr(zipfile.length() + 1));
|
||||
struct zip* z = zip_open(zipfile.c_str(), ZIP_CHECKCONS, NULL);
|
||||
{
|
||||
struct zip_stat info;
|
||||
zip_stat_init( &info ); // TODO: Docs unclear if zip_stat_init is required.
|
||||
zip_stat( z, desiredFile.c_str(), ZIP_FL_NOCASE, &info );
|
||||
length = info.size;
|
||||
// sets machines up to use MMX, if they want
|
||||
data = System::alignedMalloc(length, 16);
|
||||
struct zip_file *zf = zip_fopen( z, desiredFile.c_str(), ZIP_FL_NOCASE );
|
||||
{
|
||||
int test = zip_fread( zf, data, length );
|
||||
debugAssertM((size_t)test == length,
|
||||
desiredFile + " was corrupt because it unzipped to the wrong size.");
|
||||
(void)test;
|
||||
zip_stat(z, internalFile.c_str(), ZIP_FL_NOCASE, &info);
|
||||
|
||||
// Add NULL termination
|
||||
char* buffer = reinterpret_cast<char*>(System::alignedMalloc(info.size + 1, 16));
|
||||
buffer[info.size] = '\0';
|
||||
|
||||
struct zip_file* zf = zip_fopen( z, internalFile.c_str(), ZIP_FL_NOCASE );
|
||||
if (zf == NULL) {
|
||||
throw std::string("\"") + internalFile + "\" inside \"" + zipfile + "\" could not be opened.";
|
||||
} else {
|
||||
const int64 bytesRead = zip_fread( zf, buffer, (info.size));
|
||||
debugAssertM((int64)bytesRead == (int64)info.size,
|
||||
internalFile + " was corrupt because it unzipped to the wrong size.");
|
||||
(void)bytesRead;
|
||||
zip_fclose( zf );
|
||||
}
|
||||
zip_fclose( zf );
|
||||
// Copy the string
|
||||
s = buffer;
|
||||
System::alignedFree(buffer);
|
||||
}
|
||||
zip_close( z );
|
||||
} else {
|
||||
data = NULL;
|
||||
}
|
||||
#else
|
||||
data = NULL;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void zipClose(void* data) {
|
||||
System::alignedFree(data);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
@@ -186,25 +176,25 @@ int64 fileLength(const std::string& filename) {
|
||||
|
||||
if (result == -1) {
|
||||
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
|
||||
std::string zip, contents;
|
||||
if(zipfileExists(filename, zip, contents)){
|
||||
int64 requiredMem;
|
||||
std::string zip, contents;
|
||||
if(zipfileExists(filename, zip, contents)){
|
||||
int64 requiredMem;
|
||||
|
||||
struct zip *z = zip_open( zip.c_str(), ZIP_CHECKCONS, NULL );
|
||||
debugAssertM(z != NULL, zip + ": zip open failed.");
|
||||
{
|
||||
{
|
||||
struct zip_stat info;
|
||||
zip_stat_init( &info ); // TODO: Docs unclear if zip_stat_init is required.
|
||||
int success = zip_stat( z, contents.c_str(), ZIP_FL_NOCASE, &info );
|
||||
(void)success;
|
||||
(void)success;
|
||||
debugAssertM(success == 0, zip + ": " + contents + ": zip stat failed.");
|
||||
requiredMem = info.size;
|
||||
}
|
||||
}
|
||||
zip_close( z );
|
||||
return requiredMem;
|
||||
} else {
|
||||
return requiredMem;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
@@ -251,9 +241,9 @@ void writeWholeFile(
|
||||
void createDirectory(
|
||||
const std::string& dir) {
|
||||
|
||||
if (dir == "") {
|
||||
return;
|
||||
}
|
||||
if (dir == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string d;
|
||||
|
||||
@@ -292,11 +282,11 @@ void createDirectory(
|
||||
if (! FileSystem::exists(p)) {
|
||||
// Windows only requires one argument to mkdir,
|
||||
// where as unix also requires the permissions.
|
||||
# ifndef G3D_WIN32
|
||||
# ifndef G3D_WINDOWS
|
||||
mkdir(p.c_str(), 0777);
|
||||
# else
|
||||
# else
|
||||
_mkdir(p.c_str());
|
||||
# endif
|
||||
# endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,39 +298,40 @@ void createDirectory(
|
||||
/* Helper methods for zipfileExists()*/
|
||||
// Given a string (the drive) and an array (the path), computes the directory
|
||||
static void _zip_resolveDirectory(std::string& completeDir, const std::string& drive, const Array<std::string>& path, const int length){
|
||||
completeDir = drive;
|
||||
int tempLength;
|
||||
// if the given length is longer than the array, we correct it
|
||||
if(length > path.length()){
|
||||
tempLength = path.length();
|
||||
} else{
|
||||
tempLength = length;
|
||||
}
|
||||
completeDir = drive;
|
||||
int tempLength;
|
||||
// if the given length is longer than the array, we correct it
|
||||
if(length > path.length()){
|
||||
tempLength = path.length();
|
||||
} else{
|
||||
tempLength = length;
|
||||
}
|
||||
|
||||
for(int t = 0; t < tempLength; ++t){
|
||||
if(t > 0){
|
||||
completeDir += "/";
|
||||
}
|
||||
completeDir += path[t];
|
||||
}
|
||||
for(int t = 0; t < tempLength; ++t){
|
||||
if(t > 0){
|
||||
completeDir += "/";
|
||||
}
|
||||
completeDir += path[t];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// assumes that zipDir references a .zip file
|
||||
/** assumes that zipDir references a .zip file */
|
||||
static bool _zip_zipContains(const std::string& zipDir, const std::string& desiredFile){
|
||||
struct zip *z = zip_open( zipDir.c_str(), ZIP_CHECKCONS, NULL );
|
||||
//the last parameter, an int, determines case sensitivity:
|
||||
//1 is sensitive, 2 is not, 0 is default
|
||||
//the last parameter, an int, determines case sensitivity:
|
||||
//1 is sensitive, 2 is not, 0 is default
|
||||
int test = zip_name_locate( z, desiredFile.c_str(), ZIP_FL_NOCASE );
|
||||
zip_close( z );
|
||||
if(test == -1){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
if(test == -1){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// If no zipfile exists, outZipfile and outInternalFile are unchanged
|
||||
|
||||
/** If no zipfile exists, outZipfile and outInternalFile are unchanged */
|
||||
bool zipfileExists(const std::string& filename, std::string& outZipfile,
|
||||
std::string& outInternalFile){
|
||||
#if _HAVE_ZIP /* G3DFIX: Use ZIP-library only if defined */
|
||||
@@ -355,7 +346,7 @@ bool zipfileExists(const std::string& filename, std::string& outZipfile,
|
||||
infile = base + ext;
|
||||
}
|
||||
|
||||
// Remove "." from path
|
||||
// Remove all standalone single dots (".") from path
|
||||
for (int i = 0; i < path.length(); ++i) {
|
||||
if (path[i] == ".") {
|
||||
path.remove(i);
|
||||
@@ -363,7 +354,7 @@ bool zipfileExists(const std::string& filename, std::string& outZipfile,
|
||||
}
|
||||
}
|
||||
|
||||
// Remove ".." from path
|
||||
// Remove non-leading ".." from path
|
||||
for (int i = 1; i < path.length(); ++i) {
|
||||
if ((path[i] == "..") && (i > 0) && (path[i - 1] != "..")) {
|
||||
// Remove both i and i - 1
|
||||
@@ -405,11 +396,15 @@ bool zipfileExists(const std::string& filename, std::string& outZipfile,
|
||||
}
|
||||
|
||||
}
|
||||
#else
|
||||
(void)filename;
|
||||
(void)outZipfile;
|
||||
(void)outInternalFile;
|
||||
#endif
|
||||
// not a valid directory structure ever,
|
||||
// obviously no .zip was found within the path
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -418,11 +413,14 @@ std::string generateFilenameBase(const std::string& prefix, const std::string& s
|
||||
|
||||
// Note "template" is a reserved word in C++
|
||||
std::string templat = prefix + System::currentDateString() + "_";
|
||||
FileSystem::getFiles(templat + "*", exist);
|
||||
FileSystem::getFiles(templat + "*", exist, true);
|
||||
|
||||
// Remove extensions
|
||||
for (int i = 0; i < exist.size(); ++i) {
|
||||
exist[i] = filenameBase(exist[i]);
|
||||
const std::string ext = FilePath::ext(exist[i]);
|
||||
if (ext.length() > 0) {
|
||||
exist[i] = exist[i].substr(0, exist[i].length() - ext.length() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
int num = 0;
|
||||
@@ -442,7 +440,7 @@ void copyFile(
|
||||
const std::string& source,
|
||||
const std::string& dest) {
|
||||
|
||||
#ifdef G3D_WIN32
|
||||
#ifdef G3D_WINDOWS
|
||||
CopyFileA(source.c_str(), dest.c_str(), FALSE);
|
||||
#else
|
||||
// TODO: don't use BinaryInput and BinaryOutput
|
||||
@@ -492,7 +490,7 @@ void parseFilename(
|
||||
|
||||
}
|
||||
|
||||
} else if ((f.size() >= 2) & isSlash(f[0]) && isSlash(f[1])) {
|
||||
} else if ((f.size() >= 2) && isSlash(f[0]) && isSlash(f[1])) {
|
||||
|
||||
// e.g. //foo
|
||||
root = f.substr(0, 2);
|
||||
@@ -512,7 +510,7 @@ void parseFilename(
|
||||
|
||||
if (i != std::string::npos) {
|
||||
// Make sure it is after the last slash!
|
||||
size_t j = iMax(f.rfind('/'), f.rfind('\\'));
|
||||
size_t j = maxNotNPOS(f.rfind('/'), f.rfind('\\'));
|
||||
if ((j == std::string::npos) || (i > j)) {
|
||||
ext = f.substr(i + 1, f.size() - i - 1);
|
||||
f = f.substr(0, i);
|
||||
@@ -523,7 +521,7 @@ void parseFilename(
|
||||
// Pull the basename off
|
||||
{
|
||||
// Find the last slash
|
||||
size_t i = iMax(f.rfind('/'), f.rfind('\\'));
|
||||
size_t i = maxNotNPOS(f.rfind('/'), f.rfind('\\'));
|
||||
|
||||
if (i == std::string::npos) {
|
||||
|
||||
@@ -556,7 +554,7 @@ void parseFilename(
|
||||
j = f.size();
|
||||
}
|
||||
|
||||
cur = iMin(i, j);
|
||||
cur = min(i, j);
|
||||
|
||||
if (cur == std::string::npos) {
|
||||
cur = f.size();
|
||||
@@ -576,9 +574,9 @@ void parseFilename(
|
||||
@param includePath If true, the names include paths
|
||||
*/
|
||||
static void getFileOrDirListNormal
|
||||
(const std::string& filespec,
|
||||
Array<std::string>& files,
|
||||
bool wantFiles,
|
||||
(const std::string& filespec,
|
||||
Array<std::string>& files,
|
||||
bool wantFiles,
|
||||
bool includePath) {
|
||||
|
||||
bool test = wantFiles ? true : false;
|
||||
@@ -614,11 +612,11 @@ static void getFileOrDirListNormal
|
||||
path = path.substr(0, path.size() - 1);
|
||||
}
|
||||
|
||||
# ifdef G3D_WIN32
|
||||
# ifdef G3D_WINDOWS
|
||||
{
|
||||
struct _finddata_t fileinfo;
|
||||
|
||||
long handle = _findfirst(filespec.c_str(), &fileinfo);
|
||||
long handle = (long)_findfirst(filespec.c_str(), &fileinfo);
|
||||
int result = handle;
|
||||
|
||||
while (result != -1) {
|
||||
@@ -695,10 +693,10 @@ static void getFileOrDirListNormal
|
||||
*/
|
||||
static void _zip_addEntry(const std::string& path,
|
||||
const std::string& prefix,
|
||||
const std::string& file,
|
||||
Set<std::string>& files,
|
||||
bool wantFiles,
|
||||
bool includePath) {
|
||||
const std::string& file,
|
||||
Set<std::string>& files,
|
||||
bool wantFiles,
|
||||
bool includePath) {
|
||||
|
||||
// Make certain we are within the desired parent folder (prefix)
|
||||
if (beginsWith(file, prefix)) {
|
||||
@@ -740,6 +738,7 @@ static void _zip_addEntry(const std::string& path,
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void getFileOrDirListZip(const std::string& path,
|
||||
const std::string& prefix,
|
||||
Array<std::string>& files,
|
||||
@@ -761,18 +760,24 @@ static void getFileOrDirListZip(const std::string& path,
|
||||
zip_close( z );
|
||||
|
||||
fileSet.getMembers(files);
|
||||
#else
|
||||
(void)path;
|
||||
(void)prefix;
|
||||
(void)files;
|
||||
(void)wantFiles;
|
||||
(void)includePath;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void determineFileOrDirList(
|
||||
const std::string& filespec,
|
||||
Array<std::string>& files,
|
||||
bool wantFiles,
|
||||
bool includePath) {
|
||||
const std::string& filespec,
|
||||
Array<std::string>& files,
|
||||
bool wantFiles,
|
||||
bool includePath) {
|
||||
|
||||
// if it is a .zip, prefix will specify the folder within
|
||||
// whose contents we want to see
|
||||
// if it is a .zip, prefix will specify the folder within
|
||||
// whose contents we want to see
|
||||
std::string prefix = "";
|
||||
std::string path = filenamePath(filespec);
|
||||
|
||||
@@ -800,39 +805,39 @@ static void determineFileOrDirList(
|
||||
}
|
||||
|
||||
|
||||
void getFiles(const std::string& filespec,
|
||||
Array<std::string>& files,
|
||||
bool includePath) {
|
||||
void getFiles(const std::string& filespec,
|
||||
Array<std::string>& files,
|
||||
bool includePath) {
|
||||
|
||||
determineFileOrDirList(filespec, files, true, includePath);
|
||||
}
|
||||
|
||||
|
||||
void getDirs(
|
||||
const std::string& filespec,
|
||||
Array<std::string>& files,
|
||||
bool includePath) {
|
||||
const std::string& filespec,
|
||||
Array<std::string>& files,
|
||||
bool includePath) {
|
||||
|
||||
determineFileOrDirList(filespec, files, false, includePath);
|
||||
determineFileOrDirList(filespec, files, false, includePath);
|
||||
}
|
||||
|
||||
|
||||
std::string filenameBaseExt(const std::string& filename) {
|
||||
int i = filename.rfind("/");
|
||||
int j = filename.rfind("\\");
|
||||
size_t i = filename.rfind("/");
|
||||
size_t j = filename.rfind("\\");
|
||||
|
||||
if ((j > i) && (j >= 0)) {
|
||||
if ((j > i) && (j != std::string::npos)) {
|
||||
i = j;
|
||||
}
|
||||
|
||||
# ifdef G3D_WIN32
|
||||
# ifdef G3D_WINDOWS
|
||||
j = filename.rfind(":");
|
||||
if ((i == -1) && (j >= 0)) {
|
||||
if ((i == std::string::npos) && (j != std::string::npos)) {
|
||||
i = j;
|
||||
}
|
||||
# endif
|
||||
|
||||
if (i == -1) {
|
||||
if (i == std::string::npos) {
|
||||
return filename;
|
||||
} else {
|
||||
return filename.substr(i + 1, filename.length() - i);
|
||||
@@ -852,8 +857,8 @@ std::string filenameBase(const std::string& s) {
|
||||
|
||||
|
||||
std::string filenameExt(const std::string& filename) {
|
||||
int i = filename.rfind(".");
|
||||
if (i >= 0) {
|
||||
size_t i = filename.rfind(".");
|
||||
if (i != std::string::npos) {
|
||||
return filename.substr(i + 1, filename.length() - i);
|
||||
} else {
|
||||
return "";
|
||||
@@ -862,21 +867,21 @@ std::string filenameExt(const std::string& filename) {
|
||||
|
||||
|
||||
std::string filenamePath(const std::string& filename) {
|
||||
int i = filename.rfind("/");
|
||||
int j = filename.rfind("\\");
|
||||
size_t i = filename.rfind("/");
|
||||
size_t j = filename.rfind("\\");
|
||||
|
||||
if ((j > i) && (j >= 0)) {
|
||||
if ((j > i) && (j != std::string::npos)) {
|
||||
i = j;
|
||||
}
|
||||
|
||||
# ifdef G3D_WIN32
|
||||
# ifdef G3D_WINDOWS
|
||||
j = filename.rfind(":");
|
||||
if ((i == -1) && (j >= 0)) {
|
||||
if ((i == std::string::npos) && (j != std::string::npos)) {
|
||||
i = j;
|
||||
}
|
||||
# endif
|
||||
|
||||
if (i == -1) {
|
||||
if (i == std::string::npos) {
|
||||
return "";
|
||||
} else {
|
||||
return filename.substr(0, i+1);
|
||||
@@ -886,23 +891,23 @@ std::string filenamePath(const std::string& filename) {
|
||||
|
||||
bool isZipfile(const std::string& filename) {
|
||||
|
||||
FILE* f = fopen(filename.c_str(), "r");
|
||||
if (f == NULL) {
|
||||
return false;
|
||||
}
|
||||
uint8 header[4];
|
||||
fread(header, 4, 1, f);
|
||||
|
||||
const uint8 zipHeader[4] = {0x50, 0x4b, 0x03, 0x04};
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (header[i] != zipHeader[i]) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
FILE* f = fopen(filename.c_str(), "r");
|
||||
if (f == NULL) {
|
||||
return false;
|
||||
}
|
||||
uint8 header[4];
|
||||
(void)fread(header, 4, 1, f);
|
||||
|
||||
const uint8 zipHeader[4] = {0x50, 0x4b, 0x03, 0x04};
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (header[i] != zipHeader[i]) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return true;
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -928,15 +933,8 @@ bool fileIsNewer(const std::string& src, const std::string& dst) {
|
||||
return sexists && ((! dexists) || (sts.st_mtime > dts.st_mtime));
|
||||
}
|
||||
|
||||
|
||||
Array<std::string> filesUsed() {
|
||||
Array<std::string> f;
|
||||
_internal::currentFilesUsed.getMembers(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#ifndef G3D_WIN32
|
||||
#ifndef G3D_WINDOWS
|
||||
#undef _stat
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user