Commit 5de4ef87 authored by hybrid's avatar hybrid

Merged from 1.8 branch, revisions 4393-4484: Compiler issues with certain...

Merged from 1.8 branch, revisions 4393-4484: Compiler issues with certain compilers fixed, localtime bug under windows7 fixed.

git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@4485 dfc29bdd-3216-0410-991c-e03cc46cb475
parent 1b371342
...@@ -24,6 +24,9 @@ Changes in 1.9 (not yet released) ...@@ -24,6 +24,9 @@ Changes in 1.9 (not yet released)
-------------------------- --------------------------
Changes in 1.8.1 (not yet released) Changes in 1.8.1 (not yet released)
- Fix compiling errors for c++ builder (thx @Greatwolf for many patches and @cfanderek for reminding)
- Initialized IColladaMeshWriter::GeometryWriting which was uninitialized.
- Fix linker trouble with irr::core::equalsByUl when compiling Irrlicht as managed code (thx @ Memorial76 for a report + testcase)
- Fix crashes in CCubeSceneNode::clone and CSphereSceneNode::clone (reported by marsupial) - Fix crashes in CCubeSceneNode::clone and CSphereSceneNode::clone (reported by marsupial)
- Fix the clipping in the listbox drawing which was only showing the right line of the sunken pane (reported by Mloren and Abraxas). - Fix the clipping in the listbox drawing which was only showing the right line of the sunken pane (reported by Mloren and Abraxas).
- Initialize slider in example 05 correct (reported by Zerochen) - Initialize slider in example 05 correct (reported by Zerochen)
......
...@@ -206,6 +206,7 @@ namespace scene ...@@ -206,6 +206,7 @@ namespace scene
: Properties(0), DefaultProperties(0), NameGenerator(0), DefaultNameGenerator(0) : Properties(0), DefaultProperties(0), NameGenerator(0), DefaultNameGenerator(0)
, WriteTextures(true), WriteDefaultScene(true), ExportSMaterialOnce(true) , WriteTextures(true), WriteDefaultScene(true), ExportSMaterialOnce(true)
, AmbientLight(0.f, 0.f, 0.f, 1.f) , AmbientLight(0.f, 0.f, 0.f, 1.f)
, GeometryWriting(ECGI_PER_MESH)
{ {
} }
......
...@@ -68,12 +68,12 @@ namespace gui ...@@ -68,12 +68,12 @@ namespace gui
{ {
} }
SCursorSprite( gui::IGUISpriteBank * spriteBank, s32 spriteId, const core::position2d<s32> &hotspot=core::position2d<s32>(0,0) ) SCursorSprite( gui::IGUISpriteBank * spriteBank, s32 spriteId, const core::position2d<s32> &hotspot=(core::position2d<s32>(0,0)) )
: SpriteBank(spriteBank), SpriteId(spriteId), HotSpot(hotspot) : SpriteBank(spriteBank), SpriteId(spriteId), HotSpot(hotspot)
{ {
} }
gui::IGUISpriteBank * SpriteBank; IGUISpriteBank * SpriteBank;
s32 SpriteId; s32 SpriteId;
core::position2d<s32> HotSpot; core::position2d<s32> HotSpot;
}; };
......
...@@ -1527,7 +1527,7 @@ namespace scene ...@@ -1527,7 +1527,7 @@ namespace scene
/** Scene nodes with the option isDebugObject set to true are /** Scene nodes with the option isDebugObject set to true are
not being saved. The scene is usually written to an .irr file, not being saved. The scene is usually written to an .irr file,
an xml based format. .irr files can Be edited with the Irrlicht an xml based format. .irr files can Be edited with the Irrlicht
Engine Editor, irrEdit (http://irredit.irrlicht3d.org). To Engine Editor, irrEdit (http://www.ambiera.com/irredit/). To
load .irr files again, see ISceneManager::loadScene(). load .irr files again, see ISceneManager::loadScene().
\param filename Name of the file. \param filename Name of the file.
\param userDataSerializer If you want to save some user data \param userDataSerializer If you want to save some user data
...@@ -1545,7 +1545,7 @@ namespace scene ...@@ -1545,7 +1545,7 @@ namespace scene
/** Scene nodes with the option isDebugObject set to true are /** Scene nodes with the option isDebugObject set to true are
not being saved. The scene is usually written to an .irr file, not being saved. The scene is usually written to an .irr file,
an xml based format. .irr files can Be edited with the Irrlicht an xml based format. .irr files can Be edited with the Irrlicht
Engine Editor, irrEdit (http://irredit.irrlicht3d.org). To Engine Editor, irrEdit (http://www.ambiera.com/irredit/). To
load .irr files again, see ISceneManager::loadScene(). load .irr files again, see ISceneManager::loadScene().
\param file File where the scene is saved into. \param file File where the scene is saved into.
\param userDataSerializer If you want to save some user data \param userDataSerializer If you want to save some user data
...@@ -1563,7 +1563,7 @@ namespace scene ...@@ -1563,7 +1563,7 @@ namespace scene
/** Scene nodes with the option isDebugObject set to true are /** Scene nodes with the option isDebugObject set to true are
not being saved. The scene is usually written to an .irr file, not being saved. The scene is usually written to an .irr file,
an xml based format. .irr files can Be edited with the Irrlicht an xml based format. .irr files can Be edited with the Irrlicht
Engine Editor, irrEdit (http://irredit.irrlicht3d.org). To Engine Editor, irrEdit (http://www.ambiera.com/irredit/). To
load .irr files again, see ISceneManager::loadScene(). load .irr files again, see ISceneManager::loadScene().
\param writer XMLWriter with which the scene is saved. \param writer XMLWriter with which the scene is saved.
\param currentPath Path which is used for relative file names. \param currentPath Path which is used for relative file names.
...@@ -1584,7 +1584,7 @@ namespace scene ...@@ -1584,7 +1584,7 @@ namespace scene
format, but other scene formats can be added to the engine via format, but other scene formats can be added to the engine via
ISceneManager::addExternalSceneLoader. .irr files can Be edited ISceneManager::addExternalSceneLoader. .irr files can Be edited
with the Irrlicht Engine Editor, irrEdit with the Irrlicht Engine Editor, irrEdit
(http://irredit.irrlicht3d.org) or saved directly by the engine (http://www.ambiera.com/irredit/) or saved directly by the engine
using ISceneManager::saveScene(). using ISceneManager::saveScene().
\param filename Name of the file to load from. \param filename Name of the file to load from.
\param userDataSerializer If you want to load user data \param userDataSerializer If you want to load user data
...@@ -1603,7 +1603,7 @@ namespace scene ...@@ -1603,7 +1603,7 @@ namespace scene
format, but other scene formats can be added to the engine via format, but other scene formats can be added to the engine via
ISceneManager::addExternalSceneLoader. .irr files can Be edited ISceneManager::addExternalSceneLoader. .irr files can Be edited
with the Irrlicht Engine Editor, irrEdit with the Irrlicht Engine Editor, irrEdit
(http://irredit.irrlicht3d.org) or saved directly by the engine (http://www.ambiera.com/irredit/) or saved directly by the engine
using ISceneManager::saveScene(). using ISceneManager::saveScene().
\param file File where the scene is loaded from. \param file File where the scene is loaded from.
\param userDataSerializer If you want to load user data \param userDataSerializer If you want to load user data
......
...@@ -160,6 +160,10 @@ If not defined, Windows Multimedia library is used, which offers also broad supp ...@@ -160,6 +160,10 @@ If not defined, Windows Multimedia library is used, which offers also broad supp
#ifdef NO_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ #ifdef NO_IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_
#undef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_ #undef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_
#endif #endif
// can't get this to compile currently under borland, can be removed if someone has a better solution
#if defined(__BORLANDC__)
#undef _IRR_COMPILE_WITH_DIRECTINPUT_JOYSTICK_
#endif
//! Only define _IRR_COMPILE_WITH_DIRECT3D_8_ if you have an appropriate DXSDK, e.g. Summer 2004 //! Only define _IRR_COMPILE_WITH_DIRECT3D_8_ if you have an appropriate DXSDK, e.g. Summer 2004
// #define _IRR_COMPILE_WITH_DIRECT3D_8_ // #define _IRR_COMPILE_WITH_DIRECT3D_8_
...@@ -826,5 +830,23 @@ precision will be lower but speed higher. currently X86 only ...@@ -826,5 +830,23 @@ precision will be lower but speed higher. currently X86 only
#undef _IRR_COMPILE_WITH_COLLADA_LOADER_ #undef _IRR_COMPILE_WITH_COLLADA_LOADER_
#endif #endif
#if defined(__BORLANDC__)
#include <tchar.h>
// Borland 5.5.1 does not have _strcmpi defined
#if __BORLANDC__ == 0x551
// #define _strcmpi strcmpi
#undef _tfinddata_t
#undef _tfindfirst
#undef _tfindnext
#define _tfinddata_t __tfinddata_t
#define _tfindfirst __tfindfirst
#define _tfindnext __tfindnext
typedef long intptr_t;
#endif
#endif
#endif // __IRR_COMPILE_CONFIG_H_INCLUDED__ #endif // __IRR_COMPILE_CONFIG_H_INCLUDED__
...@@ -193,6 +193,16 @@ namespace core ...@@ -193,6 +193,16 @@ namespace core
return (a + tolerance >= b) && (a - tolerance <= b); return (a + tolerance >= b) && (a - tolerance <= b);
} }
union FloatIntUnion32
{
FloatIntUnion32(float f1 = 0.0f) : f(f1) {}
// Portable sign-extraction
bool sign() const { return (i >> 31) != 0; }
irr::s32 i;
irr::f32 f;
};
//! We compare the difference in ULP's (spacing between floating-point numbers, aka ULP=1 means there exists no float between). //! We compare the difference in ULP's (spacing between floating-point numbers, aka ULP=1 means there exists no float between).
//\result true when numbers have a ULP <= maxUlpDiff AND have the same sign. //\result true when numbers have a ULP <= maxUlpDiff AND have the same sign.
inline bool equalsByUlp(f32 a, f32 b, int maxUlpDiff) inline bool equalsByUlp(f32 a, f32 b, int maxUlpDiff)
...@@ -202,18 +212,9 @@ namespace core ...@@ -202,18 +212,9 @@ namespace core
// When floats are interpreted as integers the two nearest possible float numbers differ just // When floats are interpreted as integers the two nearest possible float numbers differ just
// by one integer number. Also works the other way round, an integer of 1 interpreted as float // by one integer number. Also works the other way round, an integer of 1 interpreted as float
// is for example the smallest possible float number. // is for example the smallest possible float number.
union Float_t
{
Float_t(float f1 = 0.0f) : f(f1) {}
// Portable sign-extraction
bool sign() const { return (i >> 31) != 0; }
int i;
float f;
};
Float_t fa(a); FloatIntUnion32 fa(a);
Float_t fb(b); FloatIntUnion32 fb(b);
// Different signs, we could maybe get difference to 0, but so close to 0 using epsilons is better. // Different signs, we could maybe get difference to 0, but so close to 0 using epsilons is better.
if ( fa.sign() != fb.sign() ) if ( fa.sign() != fb.sign() )
......
...@@ -374,10 +374,19 @@ namespace io ...@@ -374,10 +374,19 @@ namespace io
xmlChar<T>() {} xmlChar<T>() {}
xmlChar<T>(char in) : c(static_cast<T>(in)) {} xmlChar<T>(char in) : c(static_cast<T>(in)) {}
xmlChar<T>(wchar_t in) : c(static_cast<T>(in)) {} xmlChar<T>(wchar_t in) : c(static_cast<T>(in)) {}
#if defined(__BORLANDC__)
// Note - removing explicit for borland was to get it to even compile.
// There haven't been any kind of tests for that besides that.
xmlChar<T>(unsigned char in) : c(static_cast<T>(in)) {}
xmlChar<T>(unsigned short in) : c(static_cast<T>(in)) {}
xmlChar<T>(unsigned int in) : c(static_cast<T>(in)) {}
xmlChar<T>(unsigned long in) : c(static_cast<T>(in)) {}
#else
explicit xmlChar<T>(unsigned char in) : c(static_cast<T>(in)) {} explicit xmlChar<T>(unsigned char in) : c(static_cast<T>(in)) {}
explicit xmlChar<T>(unsigned short in) : c(static_cast<T>(in)) {} explicit xmlChar<T>(unsigned short in) : c(static_cast<T>(in)) {}
explicit xmlChar<T>(unsigned int in) : c(static_cast<T>(in)) {} explicit xmlChar<T>(unsigned int in) : c(static_cast<T>(in)) {}
explicit xmlChar<T>(unsigned long in) : c(static_cast<T>(in)) {} explicit xmlChar<T>(unsigned long in) : c(static_cast<T>(in)) {}
#endif
operator T() const { return c; } operator T() const { return c; }
void operator=(int t) { c=static_cast<T>(t); } void operator=(int t) { c=static_cast<T>(t); }
}; };
......
...@@ -1015,7 +1015,7 @@ void C3DSMeshFileLoader::composeObject(io::IReadFile* file, const core::stringc& ...@@ -1015,7 +1015,7 @@ void C3DSMeshFileLoader::composeObject(io::IReadFile* file, const core::stringc&
SMaterialGroup group; SMaterialGroup group;
group.faceCount = CountFaces; group.faceCount = CountFaces;
group.faces = new u16[group.faceCount]; group.faces = new u16[group.faceCount];
for (u32 i=0; i<group.faceCount; ++i) for (u16 i=0; i<group.faceCount; ++i)
group.faces[i] = i; group.faces[i] = i;
MaterialGroups.push_back(group); MaterialGroups.push_back(group);
......
...@@ -109,7 +109,7 @@ private: ...@@ -109,7 +109,7 @@ private:
MaterialName = o.MaterialName; MaterialName = o.MaterialName;
faceCount = o.faceCount; faceCount = o.faceCount;
faces = new u16[faceCount]; faces = new u16[faceCount];
for (u32 i=0; i<faceCount; ++i) for (u16 i=0; i<faceCount; ++i)
faces[i] = o.faces[i]; faces[i] = o.faces[i];
} }
......
...@@ -1900,15 +1900,26 @@ public: ...@@ -1900,15 +1900,26 @@ public:
virtual core::stringw getStringW() virtual core::stringw getStringW()
{ {
return core::stringw(OverrideName.size()?OverrideName: // (note: don't try to put all this in some ?: operators, or c++ builder will choke)
Value ? Value->getName().getPath().c_str() : 0); if ( OverrideName.size() )
return core::stringw(OverrideName);
if ( Value )
return core::stringw(Value->getName().getPath().c_str());
return core::stringw(0);
} }
virtual core::stringc getString() virtual core::stringc getString()
{ {
// since texture names can be stringw we are careful with the types // since texture names can be stringw we are careful with the types
return core::stringc(OverrideName.size()?OverrideName: if ( OverrideName.size() )
Value ? Value->getName().getPath().c_str() : 0); return core::stringc(OverrideName);
if ( Value )
return core::stringc(Value->getName().getPath().c_str());
return core::stringc(0);
} }
virtual void setString(const char* text) virtual void setString(const char* text)
......
...@@ -141,7 +141,7 @@ IImage* CImageLoaderPCX::loadImage(io::IReadFile* file) const ...@@ -141,7 +141,7 @@ IImage* CImageLoaderPCX::loadImage(io::IReadFile* file) const
memset(PCXData+offset, value, cnt); memset(PCXData+offset, value, cnt);
else else
{ {
for (u32 i=0; i<cnt; ++i) for (u8 i=0; i<cnt; ++i)
{ {
PCXData[linestart+lineoffset]=value; PCXData[linestart+lineoffset]=value;
lineoffset += 3; lineoffset += 3;
......
...@@ -397,7 +397,7 @@ void CImageLoaderRGB::processFile(io::IReadFile* file, rgbStruct& rgb) const ...@@ -397,7 +397,7 @@ void CImageLoaderRGB::processFile(io::IReadFile* file, rgbStruct& rgb) const
#ifdef _IRR_RGB_FILE_INVERTED_IMAGE_ #ifdef _IRR_RGB_FILE_INVERTED_IMAGE_
// preserve the image as stored, eg, inverted // preserve the image as stored, eg, inverted
for (u32 i = 0; i < rgb.Header.Ysize; ++i) for (u16 i = 0; i < rgb.Header.Ysize; ++i)
#else #else
// invert the image to make it upright // invert the image to make it upright
for (s32 i = (s32)(rgb.Header.Ysize)-1; i>=0; --i) for (s32 i = (s32)(rgb.Header.Ysize)-1; i>=0; --i)
...@@ -414,7 +414,7 @@ void CImageLoaderRGB::processFile(io::IReadFile* file, rgbStruct& rgb) const ...@@ -414,7 +414,7 @@ void CImageLoaderRGB::processFile(io::IReadFile* file, rgbStruct& rgb) const
readRGBrow( rgb.tmpA, i, 3, file, rgb); readRGBrow( rgb.tmpA, i, 3, file, rgb);
// cycle thru all values for this row // cycle thru all values for this row
for (u32 j = 0; j < rgb.Header.Xsize; ++j) for (u16 j = 0; j < rgb.Header.Xsize; ++j)
{ {
if(rgb.Header.BPC == 1) if(rgb.Header.BPC == 1)
{ {
...@@ -510,7 +510,7 @@ void CImageLoaderRGB::readRGBrow(u8 *buf, int y, int z, io::IReadFile* file, rgb ...@@ -510,7 +510,7 @@ void CImageLoaderRGB::readRGBrow(u8 *buf, int y, int z, io::IReadFile* file, rgb
if (rgb.Header.BPC != 1) if (rgb.Header.BPC != 1)
{ {
u16* tmpbuf = reinterpret_cast<u16*>(buf); u16* tmpbuf = reinterpret_cast<u16*>(buf);
for (u32 i=0; i<rgb.Header.Xsize; ++i) for (u16 i=0; i<rgb.Header.Xsize; ++i)
tmpbuf[i] = os::Byteswap::byteswap(tmpbuf[i]); tmpbuf[i] = os::Byteswap::byteswap(tmpbuf[i]);
} }
#endif #endif
......
...@@ -110,7 +110,7 @@ void recalculateNormalsT(IMeshBuffer* buffer, bool smooth, bool angleWeighted) ...@@ -110,7 +110,7 @@ void recalculateNormalsT(IMeshBuffer* buffer, bool smooth, bool angleWeighted)
core::vector3df weight(1.f,1.f,1.f); core::vector3df weight(1.f,1.f,1.f);
if (angleWeighted) if (angleWeighted)
weight = getAngleWeight(v1,v2,v3); weight = irr::scene::getAngleWeight(v1,v2,v3); // writing irr::scene:: necessary for borland
buffer->getNormal(idx[i+0]) += weight.X*normal; buffer->getNormal(idx[i+0]) += weight.X*normal;
buffer->getNormal(idx[i+1]) += weight.Y*normal; buffer->getNormal(idx[i+1]) += weight.Y*normal;
...@@ -292,7 +292,7 @@ void recalculateTangentsT(IMeshBuffer* buffer, bool recalculateNormals, bool smo ...@@ -292,7 +292,7 @@ void recalculateTangentsT(IMeshBuffer* buffer, bool recalculateNormals, bool smo
//Angle-weighted normals look better, but are slightly more CPU intensive to calculate //Angle-weighted normals look better, but are slightly more CPU intensive to calculate
core::vector3df weight(1.f,1.f,1.f); core::vector3df weight(1.f,1.f,1.f);
if (angleWeighted) if (angleWeighted)
weight = getAngleWeight(v[i+0].Pos,v[i+1].Pos,v[i+2].Pos); weight = irr::scene::getAngleWeight(v[i+0].Pos,v[i+1].Pos,v[i+2].Pos); // writing irr::scene:: necessary for borland
core::vector3df localNormal; core::vector3df localNormal;
core::vector3df localTangent; core::vector3df localTangent;
core::vector3df localBinormal; core::vector3df localBinormal;
......
...@@ -180,7 +180,7 @@ public: ...@@ -180,7 +180,7 @@ public:
//! FrameBufferObject constructor //! FrameBufferObject constructor
COpenGLFBOTexture(const core::dimension2d<u32>& size, const io::path& name, COpenGLFBOTexture(const core::dimension2d<u32>& size, const io::path& name,
COpenGLDriver* driver = 0, const ECOLOR_FORMAT format = ECF_UNKNOWN); COpenGLDriver* driver = 0, ECOLOR_FORMAT format = ECF_UNKNOWN);
//! destructor //! destructor
virtual ~COpenGLFBOTexture(); virtual ~COpenGLFBOTexture();
......
...@@ -595,7 +595,8 @@ private: ...@@ -595,7 +595,8 @@ private:
// check source for all utf versions and convert to target data format // check source for all utf versions and convert to target data format
if (size >= 4 && data32[0] == static_cast<char32>(UTF32_BE)) if (size >= 4 && data32[0]
== static_cast<char32>(UTF32_BE))
{ {
// UTF-32, big endian // UTF-32, big endian
SourceFormat = ETF_UTF32_BE; SourceFormat = ETF_UTF32_BE;
......
...@@ -847,8 +847,14 @@ const aes_32t t_dec(r,c)[RC_LENGTH] = ...@@ -847,8 +847,14 @@ const aes_32t t_dec(r,c)[RC_LENGTH] =
w0(0x20), w0(0x40), w0(0x80), w0(0x1b), w0(0x36) w0(0x20), w0(0x40), w0(0x80), w0(0x1b), w0(0x36)
}; };
#define d_1(t,n,b,v) const t n[256] = { b(v##0) } #if defined(__BORLANDC__)
#define d_4(t,n,b,v) const t n[4][256] = { { b(v##0) }, { b(v##1) }, { b(v##2) }, { b(v##3) } } #define concat(s1, s2) s1##s2
#define d_1(t,n,b,v) const t n[256] = { b(concat(v,0)) }
#define d_4(t,n,b,v) const t n[4][256] = { { b(concat(v,0)) }, { b(concat(v,1)) }, { b(concat(v,2)) }, { b(concat(v,3)) } }
#else
#define d_1(t,n,b,v) const t n[256] = { b(v##0) }
#define d_4(t,n,b,v) const t n[4][256] = { { b(v##0) }, { b(v##1) }, { b(v##2) }, { b(v##3) } }
#endif
#else /* declare and instantiate tables for dynamic value generation in in tab.c */ #else /* declare and instantiate tables for dynamic value generation in in tab.c */
......
...@@ -57,8 +57,8 @@ ...@@ -57,8 +57,8 @@
/* define an unsigned 64-bit type */ /* define an unsigned 64-bit type */
#ifdef _MSC_VER #if defined(_MSC_VER) || defined(__BORLANDC__)
#if _MSC_VER < 1300 #if (_MSC_VER < 1300) || (__BORLANDC__ < 0x582)
typedef unsigned __int64 sha2_64t; typedef unsigned __int64 sha2_64t;
#define s_u64 ui64 #define s_u64 ui64
#elif ULONG_MAX == 0xffffffffffffffff #elif ULONG_MAX == 0xffffffffffffffff
......
...@@ -247,16 +247,22 @@ namespace os ...@@ -247,16 +247,22 @@ namespace os
struct tm * timeinfo; struct tm * timeinfo;
timeinfo = localtime(&rawtime); timeinfo = localtime(&rawtime);
ITimer::RealTimeDate date; // init with all 0 to indicate error
date.Hour=(u32)timeinfo->tm_hour; ITimer::RealTimeDate date={0};
date.Minute=(u32)timeinfo->tm_min; // at least Windows returns NULL on some illegal dates
date.Second=(u32)timeinfo->tm_sec; if (timeinfo)
date.Day=(u32)timeinfo->tm_mday; {
date.Month=(u32)timeinfo->tm_mon+1; // set useful values if succeeded
date.Year=(u32)timeinfo->tm_year+1900; date.Hour=(u32)timeinfo->tm_hour;
date.Weekday=(ITimer::EWeekday)timeinfo->tm_wday; date.Minute=(u32)timeinfo->tm_min;
date.Yearday=(u32)timeinfo->tm_yday+1; date.Second=(u32)timeinfo->tm_sec;
date.IsDST=timeinfo->tm_isdst != 0; date.Day=(u32)timeinfo->tm_mday;
date.Month=(u32)timeinfo->tm_mon+1;
date.Year=(u32)timeinfo->tm_year+1900;
date.Weekday=(ITimer::EWeekday)timeinfo->tm_wday;
date.Yearday=(u32)timeinfo->tm_yday+1;
date.IsDST=timeinfo->tm_isdst != 0;
}
return date; return date;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment