Merge pull request #340 from rh101/vfs-support

Enhance FileStream support to allow usage of virtual file systems
This commit is contained in:
HALX99 2021-04-24 16:30:26 +08:00 committed by GitHub
commit dd69c21c0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 539 additions and 359 deletions

View File

@ -68,8 +68,8 @@ static unsigned long ft_stream_read_callback(FT_Stream stream, unsigned long off
}
static void ft_stream_close_callback(FT_Stream stream) {
auto fd = (FileStream*)stream->descriptor.pointer;
if (fd) delete fd;
const auto* fd = (FileStream*)stream->descriptor.pointer;
delete fd;
stream->size = 0;
stream->descriptor.pointer = nullptr;
}
@ -174,21 +174,23 @@ bool FontFreeType::createFontObject(const std::string &fontName, float fontSize)
auto fullPath = FileUtils::getInstance()->fullPathForFilename(fontName);
if (fullPath.empty()) return false;
FileStream fs;
if (!fs.open(fullPath, FileStream::Mode::READ))
FileStream* fs = FileUtils::getInstance()->openFileStream(fullPath, FileStream::Mode::READ);
if (!fs)
{
return false;
}
std::unique_ptr<FT_StreamRec> fts(new FT_StreamRec());
fts->read = ft_stream_read_callback;
fts->close = ft_stream_close_callback;
fts->size = fs.seek(0, SEEK_END);
fs.seek(0, SEEK_SET);
fts->size = fs->seek(0, SEEK_END);
fs->seek(0, SEEK_SET);
fts->descriptor.pointer = new FileStream(std::move(fs));
fts->descriptor.pointer = fs;
FT_Open_Args args = { 0 };
args.flags = FT_OPEN_STREAM;
args.stream = fts.get();;
args.stream = fts.get();
if (FT_Open_Face(getFTLibrary(), &args, 0, &face))
return false;

View File

@ -80,7 +80,7 @@ protected:
static bool lazyInit();
static void destroy();
FileStream _fileStream;
FileStream* _fileStream = nullptr;
mp3dec_handle_t _handle;
friend class AudioDecoderManager;

View File

@ -68,7 +68,7 @@ protected:
AudioDecoderOgg();
~AudioDecoderOgg();
FileStream _fileStream;
FileStream* _fileStream;
OggVorbis_File _vf;
friend class AudioDecoderManager;

View File

@ -106,7 +106,7 @@ struct WAV_FILE
WAV_FILE_HEADER FileHeader;
AUDIO_SOURCE_FORMAT SourceFormat;
uint32_t PcmDataOffset;
cocos2d::FileStream Stream;
cocos2d::FileStream* Stream = nullptr;
};

View File

@ -128,7 +128,9 @@ namespace cocos2d {
#if !CC_USE_MPG123
do
{
if (!_fileStream.open(fullPath, FileStream::Mode::READ))
delete _fileStream;
_fileStream = FileUtils::getInstance()->openFileStream(fullPath, FileStream::Mode::READ);
if (!_fileStream)
{
ALOGE("Trouble with minimp3(1): %s\n", strerror(errno));
break;
@ -138,7 +140,7 @@ namespace cocos2d {
handle->_decIO.read = minimp3_read_r;
handle->_decIO.seek = minimp3_seek_r;
handle->_decIO.read_data = handle->_decIO.seek_data = &_fileStream;
handle->_decIO.read_data = handle->_decIO.seek_data = _fileStream;
if (mp3dec_ex_open_cb(&handle->_dec, &handle->_decIO, MP3D_SEEK_TO_SAMPLE) != 0)
{
@ -189,7 +191,7 @@ namespace cocos2d {
mpg123_replace_reader_handle(_handle, mpg123_read_r, mpg123_lseek_r, mpg123_close_r);
if (mpg123_open_handle(_handle, &_fileStream) != MPG123_OK
if (mpg123_open_handle(_handle, _fileStream) != MPG123_OK
|| mpg123_getformat(_handle, &rate, &channel, &mp3Encoding) != MPG123_OK)
{
ALOGE("Trouble with mpg123(2): %s\n", mpg123_strerror(_handle));
@ -248,7 +250,8 @@ namespace cocos2d {
delete _handle;
_handle = nullptr;
_fileStream.close();
delete _fileStream;
_fileStream = nullptr;
}
#else
if (_handle != nullptr)
@ -256,6 +259,9 @@ namespace cocos2d {
mpg123_close(_handle);
mpg123_delete(_handle);
_handle = nullptr;
delete _fileStream;
_fileStream = nullptr;
}
#endif
_isOpened = false;

View File

@ -69,7 +69,8 @@ namespace cocos2d {
bool AudioDecoderOgg::open(const std::string& fullPath)
{
if (!_fileStream.open(fullPath, FileStream::Mode::READ))
_fileStream = FileUtils::getInstance()->openFileStream(fullPath, FileStream::Mode::READ);
if (!_fileStream)
{
ALOGE("Trouble with ogg(1): %s\n", strerror(errno));
return false;

View File

@ -52,23 +52,23 @@ namespace cocos2d {
static bool wav_scan_chunk(WAV_FILE* wavf, uint32_t chunkID, void* header, void* body, uint32_t bodySize) {
auto& fs = wavf->Stream;
auto h = (WAV_CHUNK_HEADER*)header;
for (; fs.read(h, WAV_HEADER_SIZE) == WAV_HEADER_SIZE; ) {
for (; fs->read(h, WAV_HEADER_SIZE) == WAV_HEADER_SIZE; ) {
wavf->PcmDataOffset += WAV_HEADER_SIZE;
if (h->ChunkID == chunkID)
{ // chunk found
if (body)
{ // require read body?
auto readsize = (std::min)(bodySize, h->ChunkSize);
fs.read(body, readsize);
fs->read(body, readsize);
if (h->ChunkSize > bodySize)
fs.seek(h->ChunkSize - bodySize, SEEK_CUR);
fs->seek(h->ChunkSize - bodySize, SEEK_CUR);
wavf->PcmDataOffset += h->ChunkSize;
}
return true;
}
else {
// Skip other non specified chunk
fs.seek(h->ChunkSize, SEEK_CUR);
fs->seek(h->ChunkSize, SEEK_CUR);
wavf->PcmDataOffset += h->ChunkSize;
}
}
@ -76,15 +76,16 @@ namespace cocos2d {
}
static bool wav_open(const std::string& fullPath, WAV_FILE* wavf)
{
bool succeed = wavf->Stream.open(fullPath, FileStream::Mode::READ);
if (!succeed)
delete wavf->Stream;
wavf->Stream = FileUtils::getInstance()->openFileStream(fullPath, FileStream::Mode::READ);
if (!wavf->Stream)
return false;
auto& fileStream = wavf->Stream;
wavf->PcmDataOffset = 0;
// Parsing RIFF chunk
fileStream.read(&wavf->FileHeader, WAV_RIFF_SIZE);
fileStream->read(&wavf->FileHeader, WAV_RIFF_SIZE);
wavf->PcmDataOffset += WAV_RIFF_SIZE;
if (wavf->FileHeader.Riff.Format != WAV_SIGN_ID)
@ -132,13 +133,13 @@ namespace cocos2d {
if (!IsEqualGUID(fmtInfo.ExtParams.SubFormat, WAV_SUBTYPE_PCM)
&& !IsEqualGUID(fmtInfo.ExtParams.SubFormat, WAV_SUBTYPE_IEEE_FLOAT))
{
fileStream.close();
fileStream->close();
return false;
}
break;
default:
ALOGW("The wav format %d doesn't supported currently!", (int)fmtInfo.AudioFormat);
fileStream.close();
fileStream->close();
assert(false);
return false;;
}
@ -148,18 +149,21 @@ namespace cocos2d {
static int wav_read(WAV_FILE* wavf, char* pcmBuf, uint32_t bytesToRead)
{
return wavf->Stream.read(pcmBuf, bytesToRead);
return wavf->Stream->read(pcmBuf, bytesToRead);
}
static int wav_seek(WAV_FILE* wavf, int offset)
{
auto newOffset = wavf->Stream.seek(wavf->PcmDataOffset + offset, SEEK_SET);
auto newOffset = wavf->Stream->seek(wavf->PcmDataOffset + offset, SEEK_SET);
return newOffset >= wavf->PcmDataOffset ? newOffset - wavf->PcmDataOffset : -1;
}
static int wav_close(WAV_FILE* wavf)
{
return wavf->Stream.close();
const auto result = wavf->Stream->close();
delete wavf->Stream;
wavf->Stream = nullptr;
return result;
}
AudioDecoderWav::AudioDecoderWav()

View File

@ -35,6 +35,7 @@ THE SOFTWARE.
#include <sys/stat.h>
#include <inttypes.h>
#include <sstream>
#include "openssl/aes.h"
#include "openssl/modes.h"
@ -51,7 +52,7 @@ THE SOFTWARE.
#include "yasio/obstream.hpp"
#include "yasio/detail/sz.hpp"
#include "CCFileStream.h"
#include "CCPosixFileStream.h"
#define USER_DEFAULT_PLAIN_MODE 0
@ -132,10 +133,12 @@ UserDefault::UserDefault()
void UserDefault::closeFileMapping()
{
_rwmmap.reset();
#if !USER_DEFAULT_PLAIN_MODE
if (_fd != -1) {
posix_close(_fd);
_fd = -1;
}
#endif
}
bool UserDefault::getBoolForKey(const char* pKey)
@ -369,9 +372,9 @@ void UserDefault::lazyInit()
{
if (_initialized) return;
_filePath = FileUtils::getInstance()->getWritablePath() + USER_DEFAULT_FILENAME;
#if !USER_DEFAULT_PLAIN_MODE
_filePath = FileUtils::getInstance()->getNativeWritableAbsolutePath() + USER_DEFAULT_FILENAME;
// construct file mapping
_fd = posix_open(_filePath.c_str(), O_OVERLAP_FLAGS);
if (_fd == -1) {
@ -415,14 +418,22 @@ void UserDefault::lazyInit()
}
#else
pugi::xml_document doc;
pugi::xml_parse_result ret = doc.load_file(_filePath.c_str());
if (ret) {
for (auto& elem : doc.document_element())
updateValueForKey(elem.name(), elem.text().as_string());
}
else {
log("UserDefault::init load xml file: %s failed, %s", _filePath.c_str(), ret.description());
_filePath = FileUtils::getInstance()->getWritablePath() + USER_DEFAULT_FILENAME;
if (FileUtils::getInstance()->isFileExist(_filePath))
{
auto data = FileUtils::getInstance()->getDataFromFile(_filePath);
pugi::xml_parse_result ret = doc.load_buffer_inplace(data.getBytes(), data.getSize());
if (ret) {
for (auto& elem : doc.document_element())
updateValueForKey(elem.name(), elem.text().as_string());
}
else {
log("UserDefault::init load xml file: %s failed, %s", _filePath.c_str(), ret.description());
}
}
#endif
_initialized = true;
@ -474,7 +485,10 @@ void UserDefault::flush()
r.append_child(kv.first.c_str())
.append_child(pugi::xml_node_type::node_pcdata)
.set_value(kv.second.c_str());
doc.save_file(_filePath.c_str(), " ");
std::stringstream ss;
doc.save(ss, " ");
FileUtils::getInstance()->writeStringToFile(ss.str(), _filePath);
#endif
}

View File

@ -25,6 +25,7 @@
#include "network/CCDownloader-curl.h"
#include <cinttypes>
#include <set>
#include <curl/curl.h>
@ -38,7 +39,6 @@
#include "platform/CCFileStream.h"
#include "md5/md5.h"
#include "yasio/xxsocket.hpp"
#include <inttypes.h>
// **NOTE**
// In the file:
@ -87,11 +87,8 @@ namespace cocos2d { namespace network {
}
}
if (_fs)
_fs.close();
if (_fsMd5)
_fsMd5.close();
delete _fs;
delete _fsMd5;
if (_requestHeaders)
curl_slist_free_all(_requestHeaders);
@ -152,7 +149,7 @@ namespace cocos2d { namespace network {
}
}
// open file
_fs.open(_tempFileName, FileStream::Mode::APPEND);
_fs = FileUtils::getInstance()->openFileStream(_tempFileName, FileStream::Mode::APPEND);
if (!_fs)
{
_errCode = DownloadTask::ERROR_OPEN_FILE_FAILED;
@ -164,13 +161,14 @@ namespace cocos2d { namespace network {
// init md5 state
_checksumFileName = filename + ".chksum";
_fsMd5.open(_checksumFileName.c_str(), FileStream::Mode::WRITE);
if (_fsMd5.seek(0, SEEK_END) != sizeof(md5_state_s)) {
_fsMd5 = FileUtils::getInstance()->openFileStream(_checksumFileName, FileStream::Mode::WRITE);
if (_fsMd5->seek(0, SEEK_END) != sizeof(md5_state_s)) {
md5_init(&_md5State);
}
else {
_fsMd5.seek(0, SEEK_SET);
_fsMd5.read(&_md5State, sizeof(md5_state_s));
_fsMd5->seek(0, SEEK_SET);
_fsMd5->read(&_md5State, sizeof(md5_state_s));
}
ret = true;
} while (0);
@ -242,9 +240,9 @@ namespace cocos2d { namespace network {
auto bytes_transferred = size * count;
if (_fs)
if (_fs->isOpen())
{
ret = _fs.write(buffer, bytes_transferred);// fwrite(buffer, size, count, _fp);
ret = _fs->write(buffer, bytes_transferred);// fwrite(buffer, size, count, _fp);
}
else
{
@ -263,8 +261,8 @@ namespace cocos2d { namespace network {
_totalBytesReceived += ret;
::md5_append(&_md5State, buffer, bytes_transferred);
_fsMd5.seek(0, SEEK_SET);
_fsMd5.write(&_md5State, sizeof(_md5State));
_fsMd5->seek(0, SEEK_SET);
_fsMd5->write(&_md5State, sizeof(_md5State));
}
curl_easy_getinfo(_curl, CURLINFO_SPEED_DOWNLOAD, &_speed);
@ -306,10 +304,10 @@ namespace cocos2d { namespace network {
string _tempFileName;
std::string _checksumFileName;
vector<unsigned char> _buf;
FileStream _fs;
FileStream* _fs;
// calculate md5 in downloading time support
FileStream _fsMd5; // store md5 state realtime
FileStream* _fsMd5; // store md5 state realtime
md5_state_s _md5State;
@ -887,8 +885,7 @@ public:
task.progressInfo.totalBytesReceived = coTask._totalBytesReceived;
task.progressInfo.totalBytesExpected = coTask._totalBytesExpected;
task.progressInfo.speedInBytes = coTask._speed;
onTaskProgress(task,
_transferDataToBuffer);
onTaskProgress(task, _transferDataToBuffer);
coTask._bytesReceived = 0;
_currTask = nullptr;
}
@ -899,14 +896,14 @@ public:
do
{
auto pFileUtils = FileUtils::getInstance();
coTask._fs.close();
coTask._fsMd5.close();
coTask._fs->close();
coTask._fsMd5->close();
if (checkState & kCheckSumStateSucceed) // No need download
{
FileStream fsOrigin;
if (fsOrigin.open(coTask._fileName, FileStream::Mode::READ)) {
task.progressInfo.totalBytesExpected = fsOrigin.seek(0, SEEK_END);
FileStream* fsOrigin = FileUtils::getInstance()->openFileStream(coTask._fileName, FileStream::Mode::READ);
if (fsOrigin) {
task.progressInfo.totalBytesExpected = fsOrigin->seek(0, SEEK_END);
task.progressInfo.bytesReceived = task.progressInfo.totalBytesExpected;
task.progressInfo.totalBytesReceived = task.progressInfo.totalBytesExpected;
task.progressInfo.speedInBytes = task.progressInfo.totalBytesExpected;
@ -916,8 +913,10 @@ public:
pFileUtils->removeFile(coTask._tempFileName);
onTaskProgress(task,
_transferDataToBuffer);
onTaskProgress(task,_transferDataToBuffer);
delete fsOrigin;
fsOrigin = nullptr;
}
else {
coTask._errCode = DownloadTask::ERROR_ORIGIN_FILE_MISSING;
@ -926,6 +925,7 @@ public:
pFileUtils->removeFile(coTask._checksumFileName);
pFileUtils->removeFile(coTask._tempFileName);
}
break;
}

View File

@ -2,199 +2,6 @@
// Copyright (c) 2020 c4games.com
#include "platform/CCFileStream.h"
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#include "base/ZipUtils.h"
#endif
#include <sys/stat.h>
#include <assert.h>
NS_CC_BEGIN
struct PXIoF {
int(*read)(PXFileHandle& handle, void*, unsigned int);
long(*seek)(PXFileHandle& handle, long, int);
int(*close)(PXFileHandle& handle);
};
static int pfs_posix_open(const std::string& path, FileStream::Mode mode, PXFileHandle& handle)
{
switch (mode) {
case FileStream::Mode::READ:
handle._fd = posix_open(path.c_str(), O_READ_FLAGS);
break;
case FileStream::Mode::WRITE:
handle._fd = posix_open(path.c_str(), O_WRITE_FLAGS);
break;
case FileStream::Mode::APPEND:
handle._fd = posix_open(path.c_str(), O_APPEND_FLAGS);
break;
default:
handle._fd = -1;
}
return handle._fd;
}
// posix standard wrappers
static int pfs_posix_read(PXFileHandle& handle, void* buf, unsigned int size) { return static_cast<int>(posix_read(handle._fd, buf, size)); }
static long pfs_posix_seek(PXFileHandle& handle, long offst, int origin) { return posix_lseek(handle._fd, offst, origin); }
static int pfs_posix_close(PXFileHandle& handle) {
int fd = handle._fd;
if (fd != -1) {
handle._fd = -1;
return posix_close(fd);
}
return 0;
}
static PXIoF pfs_posix_iof = {
pfs_posix_read,
pfs_posix_seek,
pfs_posix_close
};
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
// android AssetManager wrappers
static int pfs_asset_read(PXFileHandle& handle, void* buf, unsigned int size) { return AAsset_read(handle._asset, buf, size); }
static long pfs_asset_seek(PXFileHandle& handle, long offst, int origin) { return AAsset_seek(handle._asset, offst, origin); }
static int pfs_asset_close(PXFileHandle& handle) {
if (handle._asset != nullptr) {
AAsset_close(handle._asset);
handle._asset = nullptr;
}
return 0;
}
static PXIoF pfs_asset_iof = {
pfs_asset_read,
pfs_asset_seek,
pfs_asset_close
};
// android obb
static int pfs_obb_read(PXFileHandle& handle, void* buf, unsigned int size) { return FileUtilsAndroid::getObbFile()->zfread(&handle._zfs, buf, size); }
static long pfs_obb_seek(PXFileHandle& handle, long offset, int origin) { return FileUtilsAndroid::getObbFile()->zfseek(&handle._zfs, offset, origin); }
static int pfs_obb_close(PXFileHandle& handle) {
FileUtilsAndroid::getObbFile()->zfclose(&handle._zfs);
return 0;
}
static PXIoF pfs_obb_iof = {
pfs_obb_read,
pfs_obb_seek,
pfs_obb_close
};
#endif
FileStream::FileStream()
{
zeroset();
}
FileStream::~FileStream()
{
this->close();
}
FileStream::FileStream(FileStream&& rhs)
{
zeroset();
assign(std::forward<FileStream>(rhs));
}
FileStream& FileStream::operator=(FileStream&& rhs)
{
assign(std::forward<FileStream>(rhs));
return *this;
}
void FileStream::zeroset()
{
memset(this, 0, sizeof(FileStream));
}
void FileStream::assign(FileStream&& rhs)
{
this->close();
memcpy(this, &rhs, sizeof(FileStream));
rhs.zeroset();
}
bool FileStream::open(const std::string& path, FileStream::Mode mode)
{
bool ok = false;
#if CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID
ok = pfs_posix_open(path, mode, _handle) != -1;
#else // Android
if (path[0] != '/') { // from package, always readonly
std::string relativePath;
size_t position = path.find("assets/");
if (0 == position)
{
// "assets/" is at the beginning of the path and we don't want it
relativePath = path.substr(sizeof("assets/") - 1);
}
else
{
relativePath = path;
}
auto obb = FileUtilsAndroid::getObbFile();
ok = obb != nullptr && obb->zfopen(relativePath, &_handle._zfs);
if (ok) {
this->_iof = &pfs_obb_iof;
}
else {
AAssetManager* asMgr = FileUtilsAndroid::getAssetManager();
AAsset* asset = AAssetManager_open(asMgr, relativePath.c_str(), AASSET_MODE_UNKNOWN);
ok = !!asset;
if (ok) {
_handle._asset = asset;
// setup file read/seek/close at here
this->_iof = &pfs_asset_iof;
}
}
}
else { // otherwise, as a absolutely path
ok = pfs_posix_open(path, mode, _handle) != -1;
}
#endif
if (ok && !_iof)
_iof = &pfs_posix_iof;
return ok;
}
FileStream::operator bool() const
{
#if CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID
return _handle._fd != -1;
#else
return _handle._fd != -1 && _handle._asset != nullptr;
#endif
}
int FileStream::close()
{
if (_iof) {
int ret = _iof->close(_handle);
_iof = nullptr;
return ret;
}
return 0;
}
int FileStream::seek(long offset, int origin)
{
return static_cast<int>(_iof->seek(_handle, offset, origin));
}
int FileStream::read(void* buf, unsigned int size)
{
return _iof->read(_handle, buf, size);
}
int FileStream::write(const void* buf, unsigned int size)
{
return static_cast<int>(posix_write(_handle._fd, buf, size));
}
NS_CC_END

View File

@ -2,78 +2,16 @@
// Copyright (c) 2020 c4games.com
#pragma once
#include "platform/CCPlatformConfig.h"
#include <string>
#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
#include <io.h>
#include <direct.h>
#else
#include <unistd.h>
#include <errno.h>
#endif
#include <fcntl.h>
#include <functional>
#include "platform/CCPlatformConfig.h"
#include "platform/CCPlatformMacros.h"
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#include "platform/android/CCFileUtils-android.h"
#include <jni.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include "base/ZipUtils.h"
#endif
#if defined(_WIN32)
#define O_READ_FLAGS O_BINARY | O_RDONLY, S_IREAD
#define O_WRITE_FLAGS O_CREAT | O_RDWR | O_BINARY | O_TRUNC, S_IWRITE | S_IREAD
#define O_APPEND_FLAGS O_APPEND | O_CREAT | O_RDWR | O_BINARY, S_IWRITE | S_IREAD
#define O_OVERLAP_FLAGS O_CREAT | O_RDWR | O_BINARY, S_IWRITE | S_IREAD
#define posix_open ::_open
#define posix_close ::_close
#define posix_lseek ::_lseek
#define posix_read ::_read
#define posix_write ::_write
#define posix_fd2fh(fd) reinterpret_cast<HANDLE>(_get_osfhandle(fd))
#define posix_fsetsize(fd, size) ::_chsize(fd, size)
#else
#define O_READ_FLAGS O_RDONLY, S_IRUSR
#define O_WRITE_FLAGS O_CREAT | O_RDWR | O_TRUNC, S_IRWXU
#define O_APPEND_FLAGS O_APPEND | O_CREAT | O_RDWR, S_IRWXU
#define O_OVERLAP_FLAGS O_CREAT | O_RDWR, S_IRWXU
#define posix_open ::open
#define posix_close ::close
#define posix_lseek ::lseek
#define posix_read ::read
#define posix_write ::write
#define posix_fd2fh(fd) (fd)
#define posix_fsetsize(fd, size) ::ftruncate(fd, size); ::lseek(fd, 0, SEEK_SET)
#endif
NS_CC_BEGIN
struct UnzFileStream;
union PXFileHandle {
int _fd = -1;
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
AAsset* _asset;
ZipFileStream _zfs;
#endif
};
struct PXIoF;
class CC_DLL FileStream {
public:
FileStream();
~FileStream();
FileStream(FileStream&& rhs);
FileStream& operator=(FileStream&& rhs);
virtual ~FileStream() = default;
enum class Mode {
READ,
@ -81,22 +19,20 @@ public:
APPEND,
};
bool open(const std::string& path, Mode mode);
int close();
virtual bool open(const std::string& path, FileStream::Mode mode) = 0;
virtual int close() = 0;
int seek(long offset, int origin);
int read(void* buf, unsigned int size);
virtual int seek(long offset, int origin) = 0;
virtual int read(void* buf, unsigned int size) = 0;
int write(const void* buf, unsigned int size);
virtual int write(const void* buf, unsigned int size) = 0;
virtual int tell() = 0;
virtual bool isOpen() const = 0;
operator bool() const;
virtual operator bool() const { return isOpen(); }
private:
void zeroset();
void assign(FileStream&& rhs);
PXFileHandle _handle;
const PXIoF* _iof;
protected:
FileStream() = default;
};
NS_CC_END

View File

@ -27,12 +27,14 @@ THE SOFTWARE.
#include "platform/CCFileUtils.h"
#include <stack>
#include <sstream>
#include "base/CCData.h"
#include "base/ccMacros.h"
#include "base/CCDirector.h"
#include "platform/CCSAXParser.h"
//#include "base/ccUtils.h"
#include "platform/CCPosixFileStream.h"
#ifdef MINIZIP_FROM_SYSTEM
#include <minizip/unzip.h>
@ -370,8 +372,9 @@ bool FileUtils::writeValueMapToFile(const ValueMap& dict, const std::string& ful
auto rootEle = doc.document_element();
generateElementForDict(dict, rootEle);
return doc.save_file(fullPath.c_str());
std::stringstream ss;
doc.save(ss, " ");
return writeStringToFile(ss.str(), fullPath);
}
bool FileUtils::writeValueVectorToFile(const ValueVector& vecData, const std::string& fullPath) const
@ -383,8 +386,9 @@ bool FileUtils::writeValueVectorToFile(const ValueVector& vecData, const std::st
auto rootEle = doc.document_element();
generateElementForArray(vecData, rootEle);
return doc.save_file(fullPath.c_str());
std::stringstream ss;
doc.save(ss, " ");
return writeStringToFile(ss.str(), fullPath);
}
static void generateElementForObject(const Value& value, pugi::xml_node& parent)
@ -1101,6 +1105,18 @@ void FileUtils::listFilesRecursivelyAsync(const std::string& dirPath, std::funct
}, std::move(callback));
}
FileStream* FileUtils::openFileStream(const std::string& filePath, FileStream::Mode mode)
{
PosixFileStream fs;
if (fs.open(filePath, mode))
{
return new PosixFileStream(std::move(fs)); // PosixFileStream is the default implementation
}
return nullptr;
}
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
// windows os implement should override in platform specific FileUtiles class
bool FileUtils::isDirectoryExistInternal(const std::string& dirPath) const

View File

@ -32,6 +32,7 @@ THE SOFTWARE.
#include <type_traits>
#include <mutex>
#include "platform/CCFileStream.h"
#include "platform/CCPlatformMacros.h"
#include "base/ccTypes.h"
#include "base/CCValue.h"
@ -449,11 +450,19 @@ public:
virtual const std::vector<std::string> getOriginalSearchPaths() const;
/**
* Gets the writable path.
* Gets the writable path that may not be in the format of an absolute path
* @return The path that can be write/read a file in
* @note This may return the same value as getNativeWritableAbsolutePath(). If you require the absolute path
* to the underlying file system, then call getNativeWritableAbsolutePath() instead.
*/
virtual std::string getWritablePath() const = 0;
/**
* Gets the writable path in the native file-system format
* @return The path that can be write/read a file in
*/
virtual std::string getNativeWritableAbsolutePath() const = 0;
/**
* Sets writable path.
*/
@ -839,6 +848,14 @@ public:
*/
virtual bool isDirectoryExistInternal(const std::string& dirPath) const;
/**
* Open a FileStream based on the implementation provided in openFileStream or its overrides
* @param filePath The path to the file
* @param mode The mode to open the file in, being READ | WRITE | APPEND
* @return Returns a pointer to the file stream
*/
virtual FileStream* openFileStream(const std::string& filePath, FileStream::Mode mode);
protected:
/**
* The default constructor.

View File

@ -471,6 +471,19 @@ namespace
}tImageSource;
#if CC_USE_PNG
void pngWriteCallback(png_structp png_ptr, png_bytep data, size_t length)
{
if (png_ptr == NULL)
return;
FileStream* fileStream = (FileStream*)png_get_io_ptr(png_ptr);
const auto check = fileStream->write(data, length);
if (check != length)
png_error(png_ptr, "Write Error");
}
static void pngReadCallback(png_structp png_ptr, png_bytep data, png_size_t length)
{
tImageSource* isource = (tImageSource*)png_get_io_ptr(png_ptr);
@ -2161,36 +2174,38 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB)
bool ret = false;
do
{
FILE *fp;
png_structp png_ptr;
png_infop info_ptr;
png_bytep *row_pointers;
auto* outStream = FileUtils::getInstance()->openFileStream(filePath, FileStream::Mode::WRITE);
fp = fopen(filePath.c_str(), "wb");
CC_BREAK_IF(nullptr == fp);
CC_BREAK_IF(nullptr == outStream);
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
if (nullptr == png_ptr)
{
fclose(fp);
delete outStream;
break;
}
info_ptr = png_create_info_struct(png_ptr);
if (nullptr == info_ptr)
{
fclose(fp);
delete outStream;
png_destroy_write_struct(&png_ptr, nullptr);
break;
}
if (setjmp(png_jmpbuf(png_ptr)))
{
fclose(fp);
delete outStream;
png_destroy_write_struct(&png_ptr, &info_ptr);
break;
}
png_init_io(png_ptr, fp);
//png_init_io(png_ptr, outStream);
png_set_write_fn(png_ptr, outStream, pngWriteCallback, nullptr);
if (!isToRGB && hasAlpha())
{
@ -2210,7 +2225,7 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB)
row_pointers = (png_bytep *)malloc(_height * sizeof(png_bytep));
if(row_pointers == nullptr)
{
fclose(fp);
delete outStream;
png_destroy_write_struct(&png_ptr, &info_ptr);
break;
}
@ -2234,7 +2249,7 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB)
uint8_t *tempData = static_cast<uint8_t*>(malloc(_width * _height * 3));
if (nullptr == tempData)
{
fclose(fp);
delete outStream;
png_destroy_write_struct(&png_ptr, &info_ptr);
free(row_pointers);
@ -2285,7 +2300,7 @@ bool Image::saveImageToPNG(const std::string& filePath, bool isToRGB)
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(fp);
delete outStream;
ret = true;
} while (0);
@ -2304,7 +2319,7 @@ bool Image::saveImageToJPG(const std::string& filePath)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * outfile; /* target file */
FileStream * outfile; /* target file */
JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
int row_stride; /* physical row width in image buffer */
@ -2312,9 +2327,12 @@ bool Image::saveImageToJPG(const std::string& filePath)
/* Now we can initialize the JPEG compression object. */
jpeg_create_compress(&cinfo);
CC_BREAK_IF((outfile = fopen(filePath.c_str(), "wb")) == nullptr);
jpeg_stdio_dest(&cinfo, outfile);
outfile = FileUtils::getInstance()->openFileStream(filePath, FileStream::Mode::WRITE);
CC_BREAK_IF(nullptr == outfile);
unsigned char* outputBuffer = nullptr;
unsigned long outputSize = 0;
jpeg_mem_dest(&cinfo, &outputBuffer, &outputSize);
cinfo.image_width = _width; /* image width and height, in pixels */
cinfo.image_height = _height;
@ -2335,7 +2353,14 @@ bool Image::saveImageToJPG(const std::string& filePath)
{
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
fclose(outfile);
delete outfile;
outfile = nullptr;
if (outputBuffer)
{
free(outputBuffer);
outputBuffer = nullptr;
}
break;
}
@ -2370,7 +2395,16 @@ bool Image::saveImageToJPG(const std::string& filePath)
}
jpeg_finish_compress(&cinfo);
fclose(outfile);
outfile->write(outputBuffer, outputSize);
delete outfile; // closes FileStream automatically
outfile = nullptr;
if (outputBuffer)
{
free(outputBuffer);
outputBuffer = nullptr;
}
jpeg_destroy_compress(&cinfo);
ret = true;

View File

@ -0,0 +1,188 @@
// Copyright (c) 2018-2019 HALX99.
// Copyright (c) 2020 c4games.com
#include "platform/CCPosixFileStream.h"
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#include "base/ZipUtils.h"
#endif
#include <sys/stat.h>
#include <assert.h>
NS_CC_BEGIN
struct PXIoF {
int(*read)(PXFileHandle& handle, void*, unsigned int);
long(*seek)(PXFileHandle& handle, long, int);
int(*close)(PXFileHandle& handle);
};
static int pfs_posix_open(const std::string& path, FileStream::Mode mode, PXFileHandle& handle)
{
switch (mode) {
case FileStream::Mode::READ:
handle._fd = posix_open(path.c_str(), O_READ_FLAGS);
break;
case FileStream::Mode::WRITE:
handle._fd = posix_open(path.c_str(), O_WRITE_FLAGS);
break;
case FileStream::Mode::APPEND:
handle._fd = posix_open(path.c_str(), O_APPEND_FLAGS);
break;
default:
handle._fd = -1;
}
return handle._fd;
}
// posix standard wrappers
static int pfs_posix_read(PXFileHandle& handle, void* buf, unsigned int size) { return static_cast<int>(posix_read(handle._fd, buf, size)); }
static long pfs_posix_seek(PXFileHandle& handle, long offst, int origin) { return posix_lseek(handle._fd, offst, origin); }
static int pfs_posix_close(PXFileHandle& handle) {
int fd = handle._fd;
if (fd != -1) {
handle._fd = -1;
return posix_close(fd);
}
return 0;
}
static PXIoF pfs_posix_iof = {
pfs_posix_read,
pfs_posix_seek,
pfs_posix_close
};
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
// android AssetManager wrappers
static int pfs_asset_read(PXFileHandle& handle, void* buf, unsigned int size) { return AAsset_read(handle._asset, buf, size); }
static long pfs_asset_seek(PXFileHandle& handle, long offst, int origin) { return AAsset_seek(handle._asset, offst, origin); }
static int pfs_asset_close(PXFileHandle& handle) {
if (handle._asset != nullptr) {
AAsset_close(handle._asset);
handle._asset = nullptr;
}
return 0;
}
static PXIoF pfs_asset_iof = {
pfs_asset_read,
pfs_asset_seek,
pfs_asset_close
};
// android obb
static int pfs_obb_read(PXFileHandle& handle, void* buf, unsigned int size) { return FileUtilsAndroid::getObbFile()->zfread(&handle._zfs, buf, size); }
static long pfs_obb_seek(PXFileHandle& handle, long offset, int origin) { return FileUtilsAndroid::getObbFile()->zfseek(&handle._zfs, offset, origin); }
static int pfs_obb_close(PXFileHandle& handle) {
FileUtilsAndroid::getObbFile()->zfclose(&handle._zfs);
return 0;
}
static PXIoF pfs_obb_iof = {
pfs_obb_read,
pfs_obb_seek,
pfs_obb_close
};
#endif
PosixFileStream::~PosixFileStream()
{
internalClose();
}
bool PosixFileStream::open(const std::string& path, FileStream::Mode mode)
{
bool ok = false;
#if CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID
ok = pfs_posix_open(path, mode, _handle) != -1;
#else // Android
if (path[0] != '/') { // from package, always readonly
std::string relativePath;
size_t position = path.find("assets/");
if (0 == position)
{
// "assets/" is at the beginning of the path and we don't want it
relativePath = path.substr(sizeof("assets/") - 1);
}
else
{
relativePath = path;
}
auto obb = FileUtilsAndroid::getObbFile();
ok = obb != nullptr && obb->zfopen(relativePath, &_handle._zfs);
if (ok) {
this->_iof = &pfs_obb_iof;
}
else {
AAssetManager* asMgr = FileUtilsAndroid::getAssetManager();
AAsset* asset = AAssetManager_open(asMgr, relativePath.c_str(), AASSET_MODE_UNKNOWN);
ok = !!asset;
if (ok) {
_handle._asset = asset;
// setup file read/seek/close at here
this->_iof = &pfs_asset_iof;
}
}
}
else { // otherwise, as a absolutely path
ok = pfs_posix_open(path, mode, _handle) != -1;
}
#endif
if (ok && !_iof)
_iof = &pfs_posix_iof;
return ok;
}
int PosixFileStream::internalClose()
{
if (_iof) {
int ret = _iof->close(_handle);
reset();
return ret;
}
return 0;
}
int PosixFileStream::close()
{
return internalClose();
}
int PosixFileStream::seek(long offset, int origin)
{
return static_cast<int>(_iof->seek(_handle, offset, origin));
}
int PosixFileStream::read(void* buf, unsigned int size)
{
return _iof->read(_handle, buf, size);
}
int PosixFileStream::write(const void* buf, unsigned int size)
{
return static_cast<int>(posix_write(_handle._fd, buf, size));
}
int PosixFileStream::tell()
{
return static_cast<int>(_iof->seek(_handle, 0, SEEK_CUR));
}
bool PosixFileStream::isOpen() const
{
#if CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID
return _handle._fd != -1;
#else
return _handle._fd != -1 && _handle._asset != nullptr;
#endif
}
void PosixFileStream::reset()
{
memset(&_handle, 0, sizeof(_handle));
_handle._fd = -1;
_iof = nullptr;
}
NS_CC_END

View File

@ -0,0 +1,125 @@
// Copyright (c) 2018-2019 HALX99
// Copyright (c) 2020 c4games.com
#pragma once
#include "platform/CCFileStream.h"
#include "platform/CCPlatformConfig.h"
#include <string>
#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32
#include <io.h>
#include <direct.h>
#else
#include <unistd.h>
#include <errno.h>
#endif
#include <fcntl.h>
#include <functional>
#include "platform/CCPlatformMacros.h"
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
#include "platform/android/CCFileUtils-android.h"
#include <jni.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include "base/ZipUtils.h"
#endif
#if defined(_WIN32)
#define O_READ_FLAGS O_BINARY | O_RDONLY, S_IREAD
#define O_WRITE_FLAGS O_CREAT | O_RDWR | O_BINARY | O_TRUNC, S_IWRITE | S_IREAD
#define O_APPEND_FLAGS O_APPEND | O_CREAT | O_RDWR | O_BINARY, S_IWRITE | S_IREAD
#define O_OVERLAP_FLAGS O_CREAT | O_RDWR | O_BINARY, S_IWRITE | S_IREAD
#define posix_open ::_open
#define posix_close ::_close
#define posix_lseek ::_lseek
#define posix_read ::_read
#define posix_write ::_write
#define posix_fd2fh(fd) reinterpret_cast<HANDLE>(_get_osfhandle(fd))
#define posix_fsetsize(fd, size) ::_chsize(fd, size)
#else
#define O_READ_FLAGS O_RDONLY, S_IRUSR
#define O_WRITE_FLAGS O_CREAT | O_RDWR | O_TRUNC, S_IRWXU
#define O_APPEND_FLAGS O_APPEND | O_CREAT | O_RDWR, S_IRWXU
#define O_OVERLAP_FLAGS O_CREAT | O_RDWR, S_IRWXU
#define posix_open ::open
#define posix_close ::close
#define posix_lseek ::lseek
#define posix_read ::read
#define posix_write ::write
#define posix_fd2fh(fd) (fd)
#define posix_fsetsize(fd, size) ::ftruncate(fd, size); ::lseek(fd, 0, SEEK_SET)
#endif
NS_CC_BEGIN
struct UnzFileStream;
union PXFileHandle {
int _fd = -1;
#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
AAsset* _asset;
ZipFileStream _zfs;
#endif
};
struct PXIoF;
class CC_DLL PosixFileStream : public FileStream
{
public:
PosixFileStream() = default;
virtual ~PosixFileStream();
PosixFileStream(const PosixFileStream& other) = delete;
PosixFileStream(PosixFileStream&& other) noexcept
: FileStream(std::move(other)),
_handle(std::move(other._handle)),
_iof(other._iof)
{
other.reset();
}
PosixFileStream& operator=(const PosixFileStream& other) = delete;
PosixFileStream& operator=(PosixFileStream&& other) noexcept
{
if (this == &other)
return *this;
FileStream::operator =(std::move(other));
_handle = std::move(other._handle);
_iof = other._iof;
other.reset();
return *this;
}
enum class Mode {
READ,
WRITE,
APPEND,
};
bool open(const std::string& path, FileStream::Mode mode) override;
int close() override;
int seek(long offset, int origin) override;
int read(void* buf, unsigned int size) override;
int write(const void* buf, unsigned int size) override;
int tell() override;
bool isOpen() const override;
private:
int internalClose();
void reset();
PXFileHandle _handle;
const PXIoF* _iof;
};
NS_CC_END

View File

@ -160,6 +160,7 @@ set(COCOS_PLATFORM_HEADER
platform/CCSAXParser.h
platform/CCStdC.h
platform/CCFileStream.h
platform/CCPosixFileStream.h
)
set(COCOS_PLATFORM_SRC
@ -169,4 +170,5 @@ set(COCOS_PLATFORM_SRC
platform/CCFileUtils.cpp
platform/CCImage.cpp
platform/CCFileStream.cpp
platform/CCPosixFileStream.cpp
)

View File

@ -388,13 +388,18 @@ FileUtils::Status FileUtilsAndroid::getContents(const std::string& filename, Res
}
std::string FileUtilsAndroid::getWritablePath() const
{
return getNativeWritableAbsolutePath();
}
std::string FileUtilsAndroid::getNativeWritableAbsolutePath() const
{
// Fix for Nexus 10 (Android 4.2 multi-user environment)
// the path is retrieved through Java Context.getCacheDir() method
std::string path = JniHelper::callStaticStringMethod("org.cocos2dx.lib.Cocos2dxHelper", "getCocos2dxWritablePath");
if (!path.empty())
path.append("/");
return path;
}

View File

@ -68,6 +68,7 @@ public:
virtual FileUtils::Status getContents(const std::string& filename, ResizableBuffer* buffer) const override;
virtual std::string getWritablePath() const override;
std::string getNativeWritableAbsolutePath() const override;
virtual bool isAbsolutePath(const std::string& strPath) const override;
virtual int64_t getFileSize(const std::string& filepath) const override;

View File

@ -50,6 +50,7 @@ public:
virtual ~FileUtilsApple();
/* override functions */
virtual std::string getWritablePath() const override;
virtual std::string getNativeWritableAbsolutePath() const override;
virtual std::string getFullPathForFilenameWithinDirectory(const std::string& directory, const std::string& filename) const override;
#if CC_FILEUTILS_APPLE_ENABLE_OBJC

View File

@ -84,6 +84,12 @@ FileUtils* FileUtils::getInstance()
std::string FileUtilsApple::getWritablePath() const
{
DECLARE_GUARD;
return getNativeWritableAbsolutePath();
}
std::string FileUtilsApple::getNativeWritableAbsolutePath() const
{
DECLARE_GUARD;
if (_writablePath.length())

View File

@ -94,6 +94,12 @@ bool FileUtilsLinux::init()
}
string FileUtilsLinux::getWritablePath() const
{
DECLARE_GUARD;
return getNativeWritableAbsolutePath();
}
std::string FileUtilsLinux::getNativeWritableAbsolutePath() const
{
DECLARE_GUARD;
struct stat st;

View File

@ -50,6 +50,8 @@ public:
/* override functions */
bool init() override;
virtual std::string getWritablePath() const override;
std::string getNativeWritableAbsolutePath() const override;
private:
virtual bool isFileExistInternal(const std::string& strFilePath) const override;
};

View File

@ -299,6 +299,12 @@ std::vector<std::string> FileUtilsWin32::listFiles(const std::string& dirPath) c
}
string FileUtilsWin32::getWritablePath() const
{
DECLARE_GUARD;
return getNativeWritableAbsolutePath();
}
std::string FileUtilsWin32::getNativeWritableAbsolutePath() const
{
DECLARE_GUARD;
if (_writablePath.length())
@ -313,9 +319,9 @@ string FileUtilsWin32::getWritablePath() const
// Debug app uses executable directory; Non-debug app uses local app data directory
//#ifndef _DEBUG
// Get filename of executable only, e.g. MyGame.exe
WCHAR *base_name = wcsrchr(full_path, '\\');
WCHAR* base_name = wcsrchr(full_path, '\\');
wstring retPath;
if(base_name)
if (base_name)
{
WCHAR app_data_path[CC_MAX_PATH + 1];

View File

@ -46,8 +46,9 @@ protected:
FileUtilsWin32();
public:
/* override functions */
bool init();
bool init() override;
virtual std::string getWritablePath() const override;
virtual std::string getNativeWritableAbsolutePath() const override;
virtual bool isAbsolutePath(const std::string& strPath) const override;
protected: