// // Lol Engine // // Copyright: (c) 2010-2014 Sam Hocevar // This program is free software; you can redistribute it and/or // modify it under the terms of the Do What The Fuck You Want To // Public License, Version 2, as published by Sam Hocevar. See // http://www.wtfpl.net/ for more details. // #include #if __ANDROID__ # include # include #endif #if defined(_WIN32) # define WIN32_LEAN_AND_MEAN 1 # include #else # include #endif #include #include namespace lol { #if __ANDROID__ extern AAssetManager *g_assets; #endif //--------------- class FileData { friend class File; FileData() : m_refcount(0), m_type(StreamType::File) { } void Open(StreamType stream) { if (m_type == StreamType::File || m_type == StreamType::FileBinary) return; m_type = stream; switch (stream.ToScalar()) { #if __ANDROID__ /* FIXME: no modes, no error checking, no nothing */ #elif HAVE_STDIO_H case StreamType::StdIn: m_fd = stdin; break; case StreamType::StdOut: m_fd = stdout; break; case StreamType::StdErr: m_fd = stderr; break; #endif } } void Open(String const &file, FileAccess mode, bool force_binary) { m_type = (force_binary) ? (StreamType::FileBinary) : (StreamType::File); #if __ANDROID__ ASSERT(g_assets); m_asset = AAssetManager_open(g_assets, file.C(), AASSET_MODE_UNKNOWN); #elif HAVE_STDIO_H /* FIXME: no modes, no error checking, no nothing */ stat(file.C(), &m_stat); String access = (mode == FileAccess::Write) ? ("w") : ("r"); if (force_binary) access += "b"; m_fd = fopen(file.C(), access.C()); #endif } inline bool IsValid() const { #if __ANDROID__ return !!m_asset; #elif HAVE_STDIO_H return !!m_fd; #else return false; #endif } void Close() { if (m_type != StreamType::File && m_type != StreamType::FileBinary) return; #if __ANDROID__ if (m_asset) AAsset_close(m_asset); m_asset = nullptr; #elif HAVE_STDIO_H if (m_fd) fclose(m_fd); m_fd = nullptr; #endif } int Read(uint8_t *buf, int count) { #if __ANDROID__ return AAsset_read(m_asset, buf, count); #elif HAVE_STDIO_H size_t done = fread(buf, 1, count, m_fd); if (done <= 0) return -1; return (int)done; #else return 0; #endif } String ReadString() { array buf; buf.resize(BUFSIZ); String ret; while (IsValid()) { int done = Read(&buf[0], buf.count()); if (done <= 0) break; int oldsize = ret.count(); ret.resize(oldsize + done); memcpy(&ret[oldsize], &buf[0], done); buf.resize(buf.count() * 3 / 2); } return ret; } int Write(uint8_t const *buf, int count) { #if __ANDROID__ //return AAsset_read(m_asset, buf, count); return 0; #elif HAVE_STDIO_H size_t done = fwrite(buf, 1, count, m_fd); if (done <= 0) return -1; return done; #else return 0; #endif } int WriteString(const String &buf) { return Write((uint8_t const *)buf.C(), buf.count()); } long int GetPosFromStart() { #if __ANDROID__ return 0; #elif HAVE_STDIO_H return ftell(m_fd); #else return 0; #endif } void SetPosFromStart(long int pos) { #if __ANDROID__ //NOT IMPLEMENTED #elif HAVE_STDIO_H fseek(m_fd, pos, SEEK_SET); #else //NOT IMPLEMENTED #endif } long int GetSize() { #if __ANDROID__ return 0; #elif HAVE_STDIO_H return m_stat.st_size; #else return 0; #endif } long int GetModificationTime() { #if __ANDROID__ return 0; #elif HAVE_STDIO_H return (long int)m_stat.st_mtime; #else return 0; #endif } //----------------------- #if __ANDROID__ AAsset *m_asset; #elif HAVE_STDIO_H FILE *m_fd; #endif std::atomic m_refcount; StreamType m_type; struct stat m_stat; }; //-- FILE -- File::File() : m_data(new FileData) { ++m_data->m_refcount; } //-- File::File(File const &that) : m_data(that.m_data) { ++m_data->m_refcount; } //-- File &File::operator =(File const &that) { if (this == &that) return *this; /* FIXME: this needs auditing */ int refcount = --m_data->m_refcount; if (refcount == 0) { m_data->Close(); delete m_data; } m_data = that.m_data; ++m_data->m_refcount; return *this; } //-- File::~File() { int refcount = --m_data->m_refcount; if (refcount == 0) { m_data->Close(); delete m_data; } } //-- void File::Open(StreamType stream) { m_data->Open(stream); } //-- void File::Open(String const &file, FileAccess mode, bool force_binary) { m_data->Open(file, mode, force_binary); } //-- bool File::IsValid() const { return m_data->IsValid(); } //-- void File::Close() { m_data->Close(); } //-- int File::Read(uint8_t *buf, int count) { return m_data->Read(buf, count); } //-- String File::ReadString() { return m_data->ReadString(); } //-- int File::Write(uint8_t const *buf, int count) { return m_data->Write(buf, count); } //-- int File::WriteString(const String &buf) { return m_data->WriteString(buf); } //-- long int File::GetPosFromStart() { return m_data->GetPosFromStart(); } //-- void File::SetPosFromStart(long int pos) { m_data->SetPosFromStart(pos); } //-- long int File::GetSize() { return m_data->GetSize(); } //-- long int File::GetModificationTime() { return m_data->GetModificationTime(); } //--------------- class DirectoryData { friend class Directory; DirectoryData() : m_type(StreamType::File) { #if __ANDROID__ /* FIXME: not implemented */ #elif defined(_WIN32) m_handle = INVALID_HANDLE_VALUE; #elif HAVE_STDIO_H m_dd = nullptr; #endif } void Open(String const &directory, FileAccess mode) { m_type = StreamType::File; #if __ANDROID__ /* FIXME: not implemented */ #elif defined(_WIN32) m_directory = directory; String filter = m_directory + String("*"); filter.replace('/', '\\', true); WIN32_FIND_DATA FindFileData; m_handle = FindFirstFile(filter.C(), &FindFileData); stat(directory.C(), &m_stat); #elif HAVE_STDIO_H m_dd = opendir(directory.C()); stat(directory.C(), &m_stat); #endif } void Close() { if (m_type != StreamType::File) return; if (IsValid()) { #if __ANDROID__ /* FIXME: not implemented */ #elif defined(_WIN32) FindClose(m_handle); #elif HAVE_STDIO_H closedir(m_dd); #endif } #if __ANDROID__ /* FIXME: not implemented */ #elif defined(_WIN32) m_handle = INVALID_HANDLE_VALUE; #elif HAVE_STDIO_H m_dd = nullptr; #endif } bool GetContentList(array* files, array* directories) { if (!IsValid()) return false; #if __ANDROID__ /* FIXME: not implemented */ #elif defined(_WIN32) String filter = m_directory + String("*"); filter.replace('/', '\\', true); WIN32_FIND_DATA find_data; HANDLE handle = FindFirstFile(filter.C(), &find_data); bool file_valid = (handle != INVALID_HANDLE_VALUE); while (file_valid) { if (find_data.cFileName[0] != '.') { // We have a directory if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (directories) *directories << String(std::string(find_data.cFileName).c_str()); } else { if (files) *files << String(find_data.cFileName); } } //Go for next one file_valid = !!FindNextFile(m_handle, &find_data); } #elif HAVE_STDIO_H /* FIXME: not implemented */ #endif return ((files && files->count()) || (directories && directories->count())); } inline bool IsValid() const { #if __ANDROID__ /* FIXME: not implemented */ #elif defined(_WIN32) return (m_handle != INVALID_HANDLE_VALUE); #elif HAVE_STDIO_H return !!m_dd; #else return false; #endif } long int GetModificationTime() { #if __ANDROID__ return 0; #elif HAVE_STDIO_H return (long int)m_stat.st_mtime; #else return 0; #endif } #if __ANDROID__ /* FIXME: not implemented */ #elif defined(_WIN32) HANDLE m_handle; String m_directory; #elif HAVE_STDIO_H DIR *m_dd; #endif std::atomic m_refcount; StreamType m_type; struct stat m_stat; }; //-- DIRECTORY -- Directory::Directory(String const &name) : m_data(new DirectoryData), m_name(name + String("/")) { ++m_data->m_refcount; } //-- Directory::Directory(Directory const &that) : m_data(that.m_data), m_name(that.m_name) { ++m_data->m_refcount; } //-- Directory &Directory::operator =(Directory const &that) { if (this == &that) return *this; /* FIXME: this needs auditing */ int refcount = --m_data->m_refcount; if (refcount == 0) { m_data->Close(); delete m_data; } m_data = that.m_data; m_name = that.m_name; ++m_data->m_refcount; return *this; } //-- Directory::~Directory() { int refcount = --m_data->m_refcount; if (refcount == 0) { m_data->Close(); delete m_data; } } //-- void Directory::Open(FileAccess mode) { return m_data->Open(m_name, mode); } //-- bool Directory::IsValid() const { return m_data->IsValid(); } //-- void Directory::Close() { m_data->Close(); } //-- bool Directory::GetContent(array* files, array* directories) { array sfiles, sdirectories; bool found_some = m_data->GetContentList(&sfiles, &sdirectories); if (directories) for (int i = 0; i < sdirectories.count(); i++) directories->push(Directory(m_name + sdirectories[i])); if (files) for (int i = 0; i < sfiles.count(); i++) files->push(m_name + sfiles[i]); return (files && files->count()) || (directories || directories->count()); } //-- bool Directory::GetContent(array& files, array& directories) { return GetContent(&files, &directories); } //-- bool Directory::GetContent(array& directories) { return GetContent(nullptr, &directories); } //-- bool Directory::GetContent(array& files) { return GetContent(&files, nullptr); } //-- String Directory::GetName() { return m_name; } //-- long int Directory::GetModificationTime() { return m_data->GetModificationTime(); } //-- String Directory::GetCurrent() { String result; #if __ANDROID__ /* FIXME: not implemented */ #elif defined(_WIN32) TCHAR buff[MAX_PATH * 2]; GetCurrentDirectory(MAX_PATH, buff); result = buff; result.replace('\\', '/', true); #elif HAVE_STDIO_H /* FIXME: not implemented */ #endif return result; } //-- bool Directory::SetCurrent(String directory) { #if __ANDROID__ /* FIXME: not implemented */ #elif defined(_WIN32) String result = directory; result.replace('/', '\\', true); return !!SetCurrentDirectory(result.C()); #elif HAVE_STDIO_H /* FIXME: not implemented */ #endif return false; } } /* namespace lol */