From b7c4c409cb93c7f0881e3f3d06ddcdf4e47f0471 Mon Sep 17 00:00:00 2001 From: Nomango <569629550@qq.com> Date: Sun, 8 Jul 2018 02:41:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0File=E7=B1=BB=E5=92=8CResourc?= =?UTF-8?q?e=E7=B1=BB=EF=BC=9B=E5=9B=BE=E7=89=87=E5=92=8C=E9=9F=B3?= =?UTF-8?q?=E4=B9=90=E8=B5=84=E6=BA=90=E7=BB=9F=E4=B8=80=E4=B8=BAResource?= =?UTF-8?q?=20map?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- core/Common/Image.cpp | 286 +++++++++----------------- core/Common/Resource.cpp | 69 +++++++ core/Node/Sprite.cpp | 43 ++-- core/Tool/File.cpp | 182 ++++++++++++++++ core/Tool/Music.cpp | 157 ++++++-------- core/Tool/Path.cpp | 178 ++-------------- core/Tool/Player.cpp | 145 ++++--------- core/e2dcommon.h | 69 +++++-- core/e2dnode.h | 23 +-- core/e2dtool.h | 123 ++++++----- project/vs2017/Easy2D.vcxproj | 2 + project/vs2017/Easy2D.vcxproj.filters | 6 + 12 files changed, 622 insertions(+), 661 deletions(-) create mode 100644 core/Common/Resource.cpp create mode 100644 core/Tool/File.cpp diff --git a/core/Common/Image.cpp b/core/Common/Image.cpp index b9c089d6..d6e9cfd6 100644 --- a/core/Common/Image.cpp +++ b/core/Common/Image.cpp @@ -3,9 +3,7 @@ #include "..\e2dtool.h" #include -static std::map s_mBitmapsFromFile; -static std::map s_mBitmapsFromResource; -static std::set s_vBitmaps; +static std::map s_mBitmapsFromResource; e2d::Image::Image() @@ -14,33 +12,33 @@ e2d::Image::Image() { } -e2d::Image::Image(const String& filePath) +e2d::Image::Image(const Resource& res) : _bitmap(nullptr) , _cropRect() { - this->open(filePath); + this->open(res); } -e2d::Image::Image(int resNameId, const String& resType) +e2d::Image::Image(const Resource& res, const Rect& cropRect) : _bitmap(nullptr) , _cropRect() { - this->open(resNameId, resType); -} - -e2d::Image::Image(const String& filePath, const Rect& cropRect) - : _bitmap(nullptr) - , _cropRect() -{ - this->open(filePath); + this->open(res); this->crop(cropRect); } -e2d::Image::Image(int resNameId, const String& resType, const Rect& cropRect) +e2d::Image::Image(const String & fileName) : _bitmap(nullptr) , _cropRect() { - this->open(resNameId, resType); + this->open(Resource(fileName)); +} + +e2d::Image::Image(const String & fileName, const Rect & cropRect) + : _bitmap(nullptr) + , _cropRect() +{ + this->open(Resource(fileName)); this->crop(cropRect); } @@ -48,33 +46,29 @@ e2d::Image::~Image() { } -bool e2d::Image::open(const String& filePath) +bool e2d::Image::open(const Resource& res) { - WARN_IF(filePath.isEmpty(), "Image open failed! Invalid file name."); + if (!res.isResource()) + { + WARN_IF(res.getFileName().isEmpty(), "Image open failed! Invalid file name."); - if (filePath.isEmpty()) - return false; + if (res.getFileName().isEmpty()) + return false; + } - if (!Image::preload(filePath)) + if (!Image::preload(res)) { WARN("Load Image from file failed!"); return false; } - this->_setBitmap(s_mBitmapsFromFile.at(filePath.getHashCode())); + this->_setBitmap(s_mBitmapsFromResource.at(res)); return true; } -bool e2d::Image::open(int resNameId, const String& resType) +bool e2d::Image::open(const String & fileName) { - if (!Image::preload(resNameId, resType)) - { - WARN("Load Image from file failed!"); - return false; - } - - this->_setBitmap(s_mBitmapsFromResource.at(resNameId)); - return true; + return open(Resource(fileName)); } void e2d::Image::crop(const Rect& cropRect) @@ -154,94 +148,9 @@ e2d::Point e2d::Image::getCropPos() const return _cropRect.origin; } -bool e2d::Image::preload(const String& filePath) +bool e2d::Image::preload(const Resource& res) { - if (s_mBitmapsFromFile.find(filePath.getHashCode()) != s_mBitmapsFromFile.end()) - { - return true; - } - - String actualFilePath = Path::findFile(filePath); - if (actualFilePath.isEmpty()) - { - return false; - } - - HRESULT hr = S_OK; - - IWICBitmapDecoder *pDecoder = nullptr; - IWICBitmapFrameDecode *pSource = nullptr; - IWICStream *pStream = nullptr; - IWICFormatConverter *pConverter = nullptr; - ID2D1Bitmap *pBitmap = nullptr; - - // 创建解码器 - hr = Renderer::getImagingFactory()->CreateDecoderFromFilename( - (LPCWSTR)actualFilePath, - nullptr, - GENERIC_READ, - WICDecodeMetadataCacheOnLoad, - &pDecoder - ); - - if (SUCCEEDED(hr)) - { - // 创建初始化框架 - hr = pDecoder->GetFrame(0, &pSource); - } - - if (SUCCEEDED(hr)) - { - // 创建图片格式转换器 - // (DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED). - hr = Renderer::getImagingFactory()->CreateFormatConverter(&pConverter); - } - - if (SUCCEEDED(hr)) - { - // 图片格式转换成 32bbpPBGRA - hr = pConverter->Initialize( - pSource, - GUID_WICPixelFormat32bppPBGRA, - WICBitmapDitherTypeNone, - nullptr, - 0.f, - WICBitmapPaletteTypeMedianCut - ); - } - - if (SUCCEEDED(hr)) - { - // 从 WIC 位图创建一个 Direct2D 位图 - hr = Renderer::getInstance()->getRenderTarget()->CreateBitmapFromWicBitmap( - pConverter, - nullptr, - &pBitmap - ); - } - - if (SUCCEEDED(hr)) - { - // 保存图片指针和图片的 Hash 名 - s_mBitmapsFromFile.insert( - std::map::value_type( - filePath.getHashCode(), - pBitmap) - ); - } - - // 释放相关资源 - SafeRelease(pDecoder); - SafeRelease(pSource); - SafeRelease(pStream); - SafeRelease(pConverter); - - return SUCCEEDED(hr); -} - -bool e2d::Image::preload(int resNameId, const String& resType) -{ - if (s_mBitmapsFromResource.find(resNameId) != s_mBitmapsFromResource.end()) + if (s_mBitmapsFromResource.find(res) != s_mBitmapsFromResource.end()) { return true; } @@ -252,63 +161,87 @@ bool e2d::Image::preload(int resNameId, const String& resType) IWICBitmapFrameDecode *pSource = nullptr; IWICStream *pStream = nullptr; IWICFormatConverter *pConverter = nullptr; - IWICBitmapScaler *pScaler = nullptr; ID2D1Bitmap *pBitmap = nullptr; + IWICImagingFactory *pImagingFactory = Renderer::getImagingFactory(); - HRSRC imageResHandle = nullptr; - HGLOBAL imageResDataHandle = nullptr; - void *pImageFile = nullptr; - DWORD imageFileSize = 0; - - // 定位资源 - imageResHandle = ::FindResourceW(HINST_THISCOMPONENT, MAKEINTRESOURCE(resNameId), (LPCWSTR)resType); - - hr = imageResHandle ? S_OK : E_FAIL; - if (SUCCEEDED(hr)) + if (res.isResource()) { - // 加载资源 - imageResDataHandle = ::LoadResource(HINST_THISCOMPONENT, imageResHandle); + HRSRC imageResHandle = nullptr; + HGLOBAL imageResDataHandle = nullptr; + void *pImageFile = nullptr; + DWORD imageFileSize = 0; - hr = imageResDataHandle ? S_OK : E_FAIL; - } - - if (SUCCEEDED(hr)) - { - // 获取文件指针,并锁定资源 - pImageFile = ::LockResource(imageResDataHandle); - - hr = pImageFile ? S_OK : E_FAIL; - } - - if (SUCCEEDED(hr)) - { - // 计算大小 - imageFileSize = ::SizeofResource(HINST_THISCOMPONENT, imageResHandle); - - hr = imageFileSize ? S_OK : E_FAIL; - } - - if (SUCCEEDED(hr)) - { - // 创建 WIC 流 - hr = Renderer::getImagingFactory()->CreateStream(&pStream); - } - - if (SUCCEEDED(hr)) - { - // 初始化流 - hr = pStream->InitializeFromMemory( - reinterpret_cast(pImageFile), - imageFileSize + // 定位资源 + imageResHandle = ::FindResourceW( + HINST_THISCOMPONENT, + MAKEINTRESOURCE(res.getResNameId()), + (LPCWSTR)res.getResType() ); - } - if (SUCCEEDED(hr)) + hr = imageResHandle ? S_OK : E_FAIL; + if (SUCCEEDED(hr)) + { + // 加载资源 + imageResDataHandle = ::LoadResource(HINST_THISCOMPONENT, imageResHandle); + + hr = imageResDataHandle ? S_OK : E_FAIL; + } + + if (SUCCEEDED(hr)) + { + // 获取文件指针,并锁定资源 + pImageFile = ::LockResource(imageResDataHandle); + + hr = pImageFile ? S_OK : E_FAIL; + } + + if (SUCCEEDED(hr)) + { + // 计算大小 + imageFileSize = ::SizeofResource(HINST_THISCOMPONENT, imageResHandle); + + hr = imageFileSize ? S_OK : E_FAIL; + } + + if (SUCCEEDED(hr)) + { + // 创建 WIC 流 + hr = pImagingFactory->CreateStream(&pStream); + } + + if (SUCCEEDED(hr)) + { + // 初始化流 + hr = pStream->InitializeFromMemory( + reinterpret_cast(pImageFile), + imageFileSize + ); + } + + if (SUCCEEDED(hr)) + { + // 创建流的解码器 + hr = pImagingFactory->CreateDecoderFromStream( + pStream, + nullptr, + WICDecodeMetadataCacheOnLoad, + &pDecoder + ); + } + } + else { - // 创建流的解码器 - hr = Renderer::getImagingFactory()->CreateDecoderFromStream( - pStream, + String actualFilePath = File(res.getFileName()).getFilePath(); + if (actualFilePath.isEmpty()) + { + return false; + } + + // 创建解码器 + hr = pImagingFactory->CreateDecoderFromFilename( + (LPCWSTR)actualFilePath, nullptr, + GENERIC_READ, WICDecodeMetadataCacheOnLoad, &pDecoder ); @@ -323,8 +256,7 @@ bool e2d::Image::preload(int resNameId, const String& resType) if (SUCCEEDED(hr)) { // 创建图片格式转换器 - // (DXGI_FORMAT_B8G8R8A8_UNORM + D2D1_ALPHA_MODE_PREMULTIPLIED). - hr = Renderer::getImagingFactory()->CreateFormatConverter(&pConverter); + hr = pImagingFactory->CreateFormatConverter(&pConverter); } if (SUCCEEDED(hr)) @@ -352,7 +284,7 @@ bool e2d::Image::preload(int resNameId, const String& resType) if (SUCCEEDED(hr)) { - s_mBitmapsFromResource.insert(std::pair(resNameId, pBitmap)); + s_mBitmapsFromResource.insert(std::make_pair(res, pBitmap)); } // 释放相关资源 @@ -360,31 +292,17 @@ bool e2d::Image::preload(int resNameId, const String& resType) SafeRelease(pSource); SafeRelease(pStream); SafeRelease(pConverter); - SafeRelease(pScaler); return SUCCEEDED(hr); } - void e2d::Image::clearCache() { - for (auto bitmap : s_mBitmapsFromFile) - { - SafeRelease(bitmap.second); - } - s_mBitmapsFromFile.clear(); - for (auto bitmap : s_mBitmapsFromResource) { SafeRelease(bitmap.second); } s_mBitmapsFromResource.clear(); - - for (auto bitmap : s_vBitmaps) - { - SafeRelease(bitmap); - } - s_vBitmaps.clear(); } void e2d::Image::_setBitmap(ID2D1Bitmap * bitmap) diff --git a/core/Common/Resource.cpp b/core/Common/Resource.cpp new file mode 100644 index 00000000..225fc0eb --- /dev/null +++ b/core/Common/Resource.cpp @@ -0,0 +1,69 @@ +#include "..\e2dtool.h" + +e2d::Resource::Resource(const String & fileName) + : _isResource(false) + , _fileName(fileName) + , _resNameId(0) + , _resType() +{ +} + +e2d::Resource::Resource(int resNameId, const String & resType) + : _isResource(true) + , _fileName() + , _resNameId(resNameId) + , _resType(resType) +{ +} + +bool e2d::Resource::isResource() const +{ + return _isResource; +} + +const e2d::String & e2d::Resource::getFileName() const +{ + return _fileName; +} + +int e2d::Resource::getResNameId() const +{ + return _resNameId; +} + +const e2d::String & e2d::Resource::getResType() const +{ + return _resType; +} + +int e2d::Resource::getKey() const +{ + if (_isResource) + { + return _resNameId; + } + else + { + return _fileName.getHashCode(); + } +} + +bool e2d::Resource::operator>(const Resource &res) const +{ + return this->getKey() > res.getKey(); +} + +bool e2d::Resource::operator>=(const Resource &res) const +{ + return this->getKey() >= res.getKey(); +} + +bool e2d::Resource::operator<(const Resource &res) const +{ + return this->getKey() < res.getKey(); +} + +bool e2d::Resource::operator<=(const Resource &res) const +{ + return this->getKey() <= res.getKey(); +} diff --git a/core/Node/Sprite.cpp b/core/Node/Sprite.cpp index 1aff8252..9fe36eca 100644 --- a/core/Node/Sprite.cpp +++ b/core/Node/Sprite.cpp @@ -12,29 +12,29 @@ e2d::Sprite::Sprite(Image * image) open(image); } -e2d::Sprite::Sprite(const String& filePath) +e2d::Sprite::Sprite(const Resource& res) : _image(nullptr) { - open(filePath); + open(res); } -e2d::Sprite::Sprite(int resNameId, const String& resType) +e2d::Sprite::Sprite(const Resource& res, const Rect& cropRect) : _image(nullptr) { - open(resNameId, resType); -} - -e2d::Sprite::Sprite(const String& filePath, const Rect& cropRect) - : _image(nullptr) -{ - open(filePath); + open(res); crop(cropRect); } -e2d::Sprite::Sprite(int resNameId, const String& resType, const Rect& cropRect) +e2d::Sprite::Sprite(const String & fileName) : _image(nullptr) { - open(resNameId, resType); + open(fileName); +} + +e2d::Sprite::Sprite(const String & fileName, const Rect & cropRect) + : _image(nullptr) +{ + open(fileName); crop(cropRect); } @@ -57,7 +57,7 @@ bool e2d::Sprite::open(Image * image) return false; } -bool e2d::Sprite::open(const String& filePath) +bool e2d::Sprite::open(const Resource& res) { if (!_image) { @@ -65,7 +65,7 @@ bool e2d::Sprite::open(const String& filePath) GC::retain(_image); } - if (_image->open(filePath)) + if (_image->open(res)) { Node::setSize(_image->getWidth(), _image->getHeight()); return true; @@ -73,20 +73,9 @@ bool e2d::Sprite::open(const String& filePath) return false; } -bool e2d::Sprite::open(int resNameId, const String& resType) +bool e2d::Sprite::open(const String & fileName) { - if (!_image) - { - _image = new (e2d::autorelease) Image(); - GC::retain(_image); - } - - if (_image->open(resNameId, resType)) - { - Node::setSize(_image->getWidth(), _image->getHeight()); - return true; - } - return false; + return open(Resource(fileName)); } void e2d::Sprite::crop(const Rect& cropRect) diff --git a/core/Tool/File.cpp b/core/Tool/File.cpp new file mode 100644 index 00000000..960a8ba5 --- /dev/null +++ b/core/Tool/File.cpp @@ -0,0 +1,182 @@ +#include "..\e2dtool.h" +#include + +std::list e2d::File::_searchPaths; + +e2d::File::File() + : _fileName() + , _attributes(0) +{ +} + +e2d::File::File(const String & fileName) + : _fileName(fileName) + , _attributes(0) +{ + this->open(fileName); +} + +e2d::File::~File() +{ +} + +bool e2d::File::open(const String & fileName) +{ + auto FindFile = [=](const String & path) -> bool + { + if (::_waccess((const wchar_t*)path, 0) == 0) + { + _attributes = ::GetFileAttributes((LPCTSTR)path); + return true; + } + return false; + }; + + if (FindFile(fileName)) + { + _fileName = fileName; + return true; + } + else + { + for (auto& resPath : _searchPaths) + { + if (FindFile(resPath + fileName)) + { + _fileName = resPath + fileName; + return true; + } + } + } + return false; +} + +bool e2d::File::exists() const +{ + return ::_waccess((const wchar_t*)_fileName, 0) == 0; +} + +bool e2d::File::isFolder() const +{ + return _attributes & FILE_ATTRIBUTE_DIRECTORY; +} + +e2d::String e2d::File::getFilePath() const +{ + return _fileName; +} + +e2d::String e2d::File::getExtension() const +{ + String fileExtension; + // 找到文件名中的最后一个 '.' 的位置 + size_t pos = _fileName.getWString().find_last_of(L'.'); + // 判断 pos 是否是有效位置 + if (pos != std::wstring::npos) + { + // 截取扩展名 + fileExtension = _fileName.subtract(static_cast(pos)); + // 转换为小写字母 + fileExtension = fileExtension.toLower(); + } + return fileExtension; +} + +bool e2d::File::deleteFile() +{ + if (::DeleteFile((LPCWSTR)_fileName)) + return true; + return false; +} + +e2d::File e2d::File::extract(int resNameId, const String & resType, const String& destFileName) +{ + String destFilePath = Path::getTempPath() + destFileName; + // 创建文件 + HANDLE hFile = ::CreateFile((LPCWSTR)destFilePath, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return std::move(File()); + + // 查找资源文件中、加载资源到内存、得到资源大小 + HRSRC hRes = ::FindResource(NULL, MAKEINTRESOURCE(resNameId), (LPCWSTR)resType); + HGLOBAL hMem = ::LoadResource(NULL, hRes); + DWORD dwSize = ::SizeofResource(NULL, hRes); + + if (hRes && hMem && dwSize) + { + // 写入文件 + DWORD dwWrite = 0; + ::WriteFile(hFile, hMem, dwSize, &dwWrite, NULL); + ::CloseHandle(hFile); + return File(destFilePath); + } + else + { + ::CloseHandle(hFile); + ::DeleteFile((LPCWSTR)destFilePath); + return std::move(File()); + } +} + +void e2d::File::addSearchPath(const String & path) +{ + String tmp = path; + tmp.replace(L"/", L"\\"); + if (tmp[tmp.getLength() - 1] != L'\\') + { + tmp << L"\\"; + } + auto iter = std::find(_searchPaths.cbegin(), _searchPaths.cend(), tmp); + if (iter == _searchPaths.cend()) + { + _searchPaths.push_front(path); + } +} + +bool e2d::File::createFolder(const String & dirPath) +{ + if (dirPath.isEmpty() || dirPath.getLength() >= MAX_PATH) + return false; + + wchar_t tmpDirPath[_MAX_PATH] = { 0 }; + int length = dirPath.getLength(); + + for (int i = 0; i < length; ++i) + { + tmpDirPath[i] = dirPath.at(i); + if (tmpDirPath[i] == L'\\' || tmpDirPath[i] == L'/' || i == (length - 1)) + { + if (::_waccess(tmpDirPath, 0) != 0) + { + if (::_wmkdir(tmpDirPath) != 0) + { + return false; + } + } + } + } + return true; +} + +e2d::String e2d::File::getSaveFilePath(const String& title, const String& defExt) +{ + // 弹出保存对话框 + OPENFILENAME ofn = { 0 }; + wchar_t strFilename[MAX_PATH] = { 0 }; // 用于接收文件名 + ofn.lStructSize = sizeof(OPENFILENAME); // 结构体大小 + ofn.hwndOwner = Window::getInstance()->getHWnd(); // 窗口句柄 + ofn.lpstrFilter = L"所有文件\0*.*\0\0"; // 设置过滤 + ofn.nFilterIndex = 1; // 过滤器索引 + ofn.lpstrFile = strFilename; // 接收返回的文件路径和文件名 + ofn.nMaxFile = sizeof(strFilename); // 缓冲区长度 + ofn.lpstrInitialDir = nullptr; // 初始目录为默认 + ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; + ofn.lpstrTitle = (LPCWSTR)title; // 标题 + ofn.lpstrDefExt = (LPCWSTR)defExt; // 默认追加的扩展名 + + if (GetSaveFileName(&ofn)) + { + return strFilename; + } + return std::move(String()); +} diff --git a/core/Tool/Music.cpp b/core/Tool/Music.cpp index dab27b85..1dc678b0 100644 --- a/core/Tool/Music.cpp +++ b/core/Tool/Music.cpp @@ -49,7 +49,7 @@ e2d::Music::Music(const e2d::String & filePath) this->open(filePath); } -e2d::Music::Music(int resNameId, const String & resType) +e2d::Music::Music(const Resource& res) : _opened(false) , _playing(false) , _wfx(nullptr) @@ -60,7 +60,7 @@ e2d::Music::Music(int resNameId, const String & resType) , _voice(nullptr) , _voiceCallback(this) { - this->open(resNameId, resType); + this->open(res); } e2d::Music::~Music() @@ -69,6 +69,11 @@ e2d::Music::~Music() } bool e2d::Music::open(const e2d::String& filePath) +{ + return open(Resource(filePath)); +} + +bool e2d::Music::open(const Resource& res) { if (_opened) { @@ -76,114 +81,68 @@ bool e2d::Music::open(const e2d::String& filePath) return false; } - if (filePath.isEmpty()) + if (res.isResource()) { - WARN("MusicInfo::open Invalid file name."); - return false; - } + HRSRC hResInfo; + HGLOBAL hResData; + DWORD dwSize; + void* pvRes; - String actualFilePath = Path::findFile(filePath); - if (actualFilePath.isEmpty()) + if (nullptr == (hResInfo = FindResourceW(HINST_THISCOMPONENT, MAKEINTRESOURCE(res.getResNameId()), (LPCWSTR)res.getResType()))) + return TraceError(L"FindResource"); + + if (nullptr == (hResData = LoadResource(HINST_THISCOMPONENT, hResInfo))) + return TraceError(L"LoadResource"); + + if (0 == (dwSize = SizeofResource(HINST_THISCOMPONENT, hResInfo))) + return TraceError(L"SizeofResource"); + + if (nullptr == (pvRes = LockResource(hResData))) + return TraceError(L"LockResource"); + + _resBuffer = new CHAR[dwSize]; + memcpy(_resBuffer, pvRes, dwSize); + + MMIOINFO mmioInfo; + ZeroMemory(&mmioInfo, sizeof(mmioInfo)); + mmioInfo.fccIOProc = FOURCC_MEM; + mmioInfo.cchBuffer = dwSize; + mmioInfo.pchBuffer = (CHAR*)_resBuffer; + + _hmmio = mmioOpen(nullptr, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ); + } + else { - WARN("MusicInfo::open File not found."); - return false; - } + String filePath = res.getFileName(); + if (filePath.isEmpty()) + { + WARN("MusicInfo::open Invalid file name."); + return false; + } - // 定位 wave 文件 - wchar_t pFilePath[MAX_PATH]; - if (!_findMediaFileCch(pFilePath, MAX_PATH, (const wchar_t *)actualFilePath)) - { - WARN("Failed to find media file: %s", pFilePath); - return false; - } + String actualFilePath = File(filePath).getFilePath(); + if (actualFilePath.isEmpty()) + { + WARN("MusicInfo::open File not found."); + return false; + } - _hmmio = mmioOpen(pFilePath, nullptr, MMIO_ALLOCBUF | MMIO_READ); + // 定位 wave 文件 + wchar_t pFilePath[MAX_PATH]; + if (!_findMediaFileCch(pFilePath, MAX_PATH, (const wchar_t *)actualFilePath)) + { + WARN("Failed to find media file: %s", pFilePath); + return false; + } + + _hmmio = mmioOpen(pFilePath, nullptr, MMIO_ALLOCBUF | MMIO_READ); + } if (nullptr == _hmmio) { return TraceError(L"mmioOpen"); } - if (!_readMMIO()) - { - // 读取非 wave 文件时 ReadMMIO 调用失败 - mmioClose(_hmmio, 0); - return TraceError(L"_readMMIO"); - } - - if (!_resetFile()) - return TraceError(L"_resetFile"); - - // 重置文件后,wave 文件的大小是 _ck.cksize - _dwSize = _ck.cksize; - - // 将样本数据读取到内存中 - _waveData = new BYTE[_dwSize]; - - if (!_read(_waveData, _dwSize)) - { - TraceError(L"Failed to read WAV data"); - SAFE_DELETE_ARRAY(_waveData); - return false; - } - - // 创建音源 - HRESULT hr = Player::getInstance()->getXAudio2()->CreateSourceVoice( - &_voice, - _wfx, - 0, - XAUDIO2_DEFAULT_FREQ_RATIO, - &this->_voiceCallback - ); - - if (FAILED(hr)) - { - TraceError(L"Create source voice error", hr); - SAFE_DELETE_ARRAY(_waveData); - return false; - } - - _opened = true; - _playing = false; - return true; -} - -bool e2d::Music::open(int resNameId, const e2d::String& resType) -{ - HRSRC hResInfo; - HGLOBAL hResData; - DWORD dwSize; - void* pvRes; - - if (_opened) - { - WARN("MusicInfo can be opened only once!"); - return false; - } - - if (nullptr == (hResInfo = FindResourceW(HINST_THISCOMPONENT, MAKEINTRESOURCE(resNameId), (LPCWSTR)resType))) - return TraceError(L"FindResource"); - - if (nullptr == (hResData = LoadResource(HINST_THISCOMPONENT, hResInfo))) - return TraceError(L"LoadResource"); - - if (0 == (dwSize = SizeofResource(HINST_THISCOMPONENT, hResInfo))) - return TraceError(L"SizeofResource"); - - if (nullptr == (pvRes = LockResource(hResData))) - return TraceError(L"LockResource"); - - _resBuffer = new CHAR[dwSize]; - memcpy(_resBuffer, pvRes, dwSize); - - MMIOINFO mmioInfo; - ZeroMemory(&mmioInfo, sizeof(mmioInfo)); - mmioInfo.fccIOProc = FOURCC_MEM; - mmioInfo.cchBuffer = dwSize; - mmioInfo.pchBuffer = (CHAR*)_resBuffer; - - _hmmio = mmioOpen(nullptr, &mmioInfo, MMIO_ALLOCBUF | MMIO_READ); - if (!_readMMIO()) { // 读取非 wave 文件时 ReadMMIO 调用失败 diff --git a/core/Tool/Path.cpp b/core/Tool/Path.cpp index f352359a..714ef319 100644 --- a/core/Tool/Path.cpp +++ b/core/Tool/Path.cpp @@ -1,6 +1,5 @@ #include "..\e2dtool.h" #include -#include extern "C" const GUID DECLSPEC_SELECTANY FOLDERID_LocalAppData = { @@ -8,49 +7,33 @@ extern "C" const GUID DECLSPEC_SELECTANY FOLDERID_LocalAppData = { }; -e2d::String e2d::Path::_tempPath; -e2d::String e2d::Path::_dataPath; -std::list e2d::Path::_paths; - - -void e2d::Path::addSearchPath(String path) -{ - path.replace(L"/", L"\\"); - if (path[path.getLength() - 1] != L'\\') - { - path << L"\\"; - } - auto iter = std::find(_paths.cbegin(), _paths.cend(), path); - if (iter == _paths.cend()) - { - _paths.push_front(path); - } -} - e2d::String e2d::Path::getDataPath() { - if (_dataPath.isEmpty()) + static String dataPath; + if (dataPath.isEmpty()) { // 设置数据的保存路径 String localAppDataPath = Path::getLocalAppDataPath(); String gameName = Game::getInstance()->getConfig()->getGameName(); if (!localAppDataPath.isEmpty() && !gameName.isEmpty()) { - _dataPath = localAppDataPath + L"\\Easy2DGameData\\" << gameName << L"\\"; + dataPath = localAppDataPath + L"\\Easy2DGameData\\" << gameName << L"\\"; - if (!Path::exists(_dataPath) && !Path::createFolder(_dataPath)) + File file(dataPath); + if (!file.exists() && !File::createFolder(dataPath)) { - _dataPath = L""; + dataPath = L""; } } - _dataPath << L"Data.ini"; + dataPath << L"Data.ini"; } - return _dataPath; + return dataPath; } e2d::String e2d::Path::getTempPath() { - if (_tempPath.isEmpty()) + static String tempPath; + if (tempPath.isEmpty()) { // 设置临时文件保存路径 wchar_t path[_MAX_PATH]; @@ -58,15 +41,16 @@ e2d::String e2d::Path::getTempPath() if (0 != ::GetTempPath(_MAX_PATH, path) && !gameName.isEmpty()) { - _tempPath << path << L"\\Easy2DGameTemp\\" << gameName << L"\\"; + tempPath << path << L"\\Easy2DGameTemp\\" << gameName << L"\\"; - if (!Path::exists(_tempPath) && !Path::createFolder(_tempPath)) + File file(tempPath); + if (!file.exists() && !File::createFolder(tempPath)) { - _tempPath = L""; + tempPath = L""; } } } - return _tempPath; + return tempPath; } e2d::String e2d::Path::getLocalAppDataPath() @@ -94,134 +78,14 @@ e2d::String e2d::Path::getLocalAppDataPath() e2d::String e2d::Path::getCurrentFilePath() { - TCHAR szPath[_MAX_PATH] = { 0 }; - if (::GetModuleFileName(nullptr, szPath, _MAX_PATH) != 0) + static String currFilePath; + if (currFilePath.isEmpty()) { - return std::move(String(szPath)); - } - return std::move(String()); -} - -e2d::String e2d::Path::findFile(const String& path) -{ - if (Path::exists(path)) - { - return path; - } - else - { - for (auto& resPath : _paths) + TCHAR szPath[_MAX_PATH] = { 0 }; + if (::GetModuleFileName(nullptr, szPath, _MAX_PATH) != 0) { - if (Path::exists(resPath + path)) - { - return resPath + path; - } + currFilePath = szPath; } } - return std::move(String()); -} - -e2d::String e2d::Path::extractResource(int resNameId, const String & resType, const String & destFileName) -{ - String destFilePath = _tempPath + destFileName; - // 创建文件 - HANDLE hFile = ::CreateFile((LPCWSTR)destFilePath, GENERIC_WRITE, NULL, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL); - if (hFile == INVALID_HANDLE_VALUE) - return std::move(String()); - - // 查找资源文件中、加载资源到内存、得到资源大小 - HRSRC hRes = ::FindResource(NULL, MAKEINTRESOURCE(resNameId), (LPCWSTR)resType); - HGLOBAL hMem = ::LoadResource(NULL, hRes); - DWORD dwSize = ::SizeofResource(NULL, hRes); - - if (hRes && hMem && dwSize) - { - // 写入文件 - DWORD dwWrite = 0; - ::WriteFile(hFile, hMem, dwSize, &dwWrite, NULL); - ::CloseHandle(hFile); - return destFilePath; - } - else - { - ::CloseHandle(hFile); - ::DeleteFile((LPCWSTR)destFilePath); - return std::move(String()); - } -} - -e2d::String e2d::Path::getFileExtension(const String& filePath) -{ - String fileExtension; - // 找到文件名中的最后一个 '.' 的位置 - size_t pos = filePath.getWString().find_last_of(L'.'); - // 判断 pos 是否是有效位置 - if (pos != std::wstring::npos) - { - // 截取扩展名 - fileExtension = filePath.subtract(static_cast(pos)); - // 转换为小写字母 - fileExtension = fileExtension.toLower(); - } - - return fileExtension; -} - -e2d::String e2d::Path::getSaveFilePath(const String& title, const String& defExt) -{ - // 弹出保存对话框 - OPENFILENAME ofn = { 0 }; - wchar_t strFilename[MAX_PATH] = { 0 }; // 用于接收文件名 - ofn.lStructSize = sizeof(OPENFILENAME); // 结构体大小 - ofn.hwndOwner = Window::getInstance()->getHWnd(); // 窗口句柄 - ofn.lpstrFilter = L"所有文件\0*.*\0\0"; // 设置过滤 - ofn.nFilterIndex = 1; // 过滤器索引 - ofn.lpstrFile = strFilename; // 接收返回的文件路径和文件名 - ofn.nMaxFile = sizeof(strFilename); // 缓冲区长度 - ofn.lpstrInitialDir = nullptr; // 初始目录为默认 - ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; - ofn.lpstrTitle = (LPCWSTR)title; // 标题 - ofn.lpstrDefExt = (LPCWSTR)defExt; // 默认追加的扩展名 - - if (GetSaveFileName(&ofn)) - { - return strFilename; - } - return std::move(String()); -} - -bool e2d::Path::createFolder(const String& dirPath) -{ - if (dirPath.isEmpty() || dirPath.getLength() >= MAX_PATH) - { - return false; - } - - wchar_t tmpDirPath[_MAX_PATH] = { 0 }; - int length = dirPath.getLength(); - - for (int i = 0; i < length; ++i) - { - tmpDirPath[i] = dirPath.at(i); - if (tmpDirPath[i] == L'\\' || tmpDirPath[i] == L'/' || i == (length - 1)) - { - if (::_waccess(tmpDirPath, 0) != 0) - { - if (::_wmkdir(tmpDirPath) != 0) - { - return false; - } - } - } - } - return true; -} - -bool e2d::Path::exists(const String & path) -{ - if (path.isEmpty() || path.getLength() >= MAX_PATH) - { - return false; - } - return ::_waccess((const wchar_t *)path, 0) == 0; + return currFilePath; } diff --git a/core/Tool/Player.cpp b/core/Tool/Player.cpp index dc807d48..aaf0ea18 100644 --- a/core/Tool/Player.cpp +++ b/core/Tool/Player.cpp @@ -13,10 +13,6 @@ e2d::Player::Player() e2d::Player::~Player() { - for (auto pair : _fileList) - GC::release(pair.second); - _fileList.clear(); - for (auto pair : _resList) GC::release(pair.second); _resList.clear(); @@ -61,67 +57,42 @@ IXAudio2 * e2d::Player::getXAudio2() bool e2d::Player::preload(const String& filePath) { - UINT hash = filePath.getHashCode(); + if (filePath.isEmpty()) + return false; - if (_fileList.end() != _fileList.find(hash)) - { - return true; - } - else - { - Music * music = new (e2d::autorelease) Music(); - - if (music->open(filePath)) - { - GC::retain(music); - music->setVolume(_volume); - _fileList.insert(std::pair(hash, music)); - return true; - } - } - return false; + return preload(Resource(filePath)); } -bool e2d::Player::preload(int resNameId, const String& resType) +bool e2d::Player::preload(const Resource& res) { - if (_resList.end() != _resList.find(resNameId)) - { + if (_resList.end() != _resList.find(res)) return true; - } - else - { - Music * music = new (e2d::autorelease) Music(); - if (music->open(resNameId, resType)) - { - GC::retain(music); - music->setVolume(_volume); - _resList.insert(std::pair(resNameId, music)); - return true; - } + Music * music = new (e2d::autorelease) Music(); + + if (music->open(res)) + { + GC::retain(music); + music->setVolume(_volume); + _resList.insert(std::make_pair(res, music)); + return true; } return false; } bool e2d::Player::play(const String& filePath, int nLoopCount) { - if (Player::preload(filePath)) - { - UINT hash = filePath.getHashCode(); - auto music = _fileList[hash]; - if (music->play(nLoopCount)) - { - return true; - } - } - return false; + if (filePath.isEmpty()) + return false; + + return play(Resource(filePath), nLoopCount); } -bool e2d::Player::play(int resNameId, const String& resType, int nLoopCount) +bool e2d::Player::play(const Resource& res, int nLoopCount) { - if (Player::preload(resNameId, resType)) + if (Player::preload(res)) { - auto music = _resList[resNameId]; + auto music = _resList[res]; if (music->play(nLoopCount)) { return true; @@ -135,16 +106,13 @@ void e2d::Player::pause(const String& filePath) if (filePath.isEmpty()) return; - UINT hash = filePath.getHashCode(); - - if (_fileList.end() != _fileList.find(hash)) - _fileList[hash]->pause(); + pause(Resource(filePath)); } -void e2d::Player::pause(int resNameId, const String& resType) +void e2d::Player::pause(const Resource& res) { - if (_resList.end() != _resList.find(resNameId)) - _resList[resNameId]->pause(); + if (_resList.end() != _resList.find(res)) + _resList[res]->pause(); } void e2d::Player::resume(const String& filePath) @@ -152,16 +120,13 @@ void e2d::Player::resume(const String& filePath) if (filePath.isEmpty()) return; - UINT hash = filePath.getHashCode(); - - if (_fileList.end() != _fileList.find(hash)) - _fileList[hash]->resume(); + resume(Resource(filePath)); } -void e2d::Player::resume(int resNameId, const String& resType) +void e2d::Player::resume(const Resource& res) { - if (_resList.end() != _resList.find(resNameId)) - _resList[resNameId]->pause(); + if (_resList.end() != _resList.find(res)) + _resList[res]->pause(); } void e2d::Player::stop(const String& filePath) @@ -169,16 +134,17 @@ void e2d::Player::stop(const String& filePath) if (filePath.isEmpty()) return; - UINT hash = filePath.getHashCode(); - - if (_fileList.end() != _fileList.find(hash)) - _fileList[hash]->stop(); + stop(Resource(filePath)); } -void e2d::Player::stop(int resNameId, const String& resType) +void e2d::Player::stop(const Resource& res) { - if (_resList.end() != _resList.find(resNameId)) - _resList[resNameId]->stop(); + if (res.isResource()) + { + + } + if (_resList.end() != _resList.find(res)) + _resList[res]->stop(); } bool e2d::Player::isPlaying(const String& filePath) @@ -186,18 +152,13 @@ bool e2d::Player::isPlaying(const String& filePath) if (filePath.isEmpty()) return false; - UINT hash = filePath.getHashCode(); - - if (_fileList.end() != _fileList.find(hash)) - return _fileList[hash]->isPlaying(); - - return false; + return isPlaying(Resource(filePath)); } -bool e2d::Player::isPlaying(int resNameId, const String& resType) +bool e2d::Player::isPlaying(const Resource& res) { - if (_resList.end() != _resList.find(resNameId)) - return _resList[resNameId]->isPlaying(); + if (_resList.end() != _resList.find(res)) + return _resList[res]->isPlaying(); return false; } @@ -209,11 +170,6 @@ double e2d::Player::getVolume() void e2d::Player::setVolume(double volume) { _volume = std::min(std::max(float(volume), -224.f), 224.f); - for (auto pair : _fileList) - { - pair.second->setVolume(_volume); - } - for (auto pair : _resList) { pair.second->setVolume(_volume); @@ -222,11 +178,6 @@ void e2d::Player::setVolume(double volume) void e2d::Player::pauseAll() { - for (auto pair : _fileList) - { - pair.second->pause(); - } - for (auto pair : _resList) { pair.second->pause(); @@ -235,11 +186,6 @@ void e2d::Player::pauseAll() void e2d::Player::resumeAll() { - for (auto pair : _fileList) - { - pair.second->resume(); - } - for (auto pair : _resList) { pair.second->resume(); @@ -248,11 +194,6 @@ void e2d::Player::resumeAll() void e2d::Player::stopAll() { - for (auto pair : _fileList) - { - pair.second->stop(); - } - for (auto pair : _resList) { pair.second->stop(); @@ -261,12 +202,6 @@ void e2d::Player::stopAll() void e2d::Player::clearCache() { - for (auto pair : _fileList) - { - GC::release(pair.second); - } - _fileList.clear(); - for (auto pair : _resList) { GC::release(pair.second); diff --git a/core/e2dcommon.h b/core/e2dcommon.h index 971b89c8..d9e1bba1 100644 --- a/core/e2dcommon.h +++ b/core/e2dcommon.h @@ -521,6 +521,44 @@ protected: }; +// 资源 +class Resource +{ +public: + Resource( + const String& fileName /* 文件路径 */ + ); + + Resource( + int resNameId, /* 资源名称 */ + const String& resType /* 资源类型 */ + ); + + // 是否是资源类型 + bool isResource() const; + + const String& getFileName() const; + + int getResNameId() const; + + const String& getResType() const; + + int getKey() const; + + // 比较运算符 + bool operator> (const Resource &) const; + bool operator>= (const Resource &) const; + bool operator< (const Resource &) const; + bool operator<= (const Resource &) const; + +protected: + bool _isResource; + String _fileName; + int _resNameId; + String _resType; +}; + + // 引用计数对象 class Ref { @@ -554,36 +592,33 @@ public: Image(); explicit Image( - const String& filePath /* 图片文件路径 */ + const Resource& res ); explicit Image( - int resNameId, /* 图片资源名称 */ - const String& resType /* 图片资源类型 */ - ); - - explicit Image( - const String& filePath, /* 图片文件路径 */ + const Resource& res, const Rect& cropRect /* 裁剪矩形 */ ); explicit Image( - int resNameId, /* 图片资源名称 */ - const String& resType, /* 图片资源类型 */ + const String& fileName + ); + + explicit Image( + const String& fileName, const Rect& cropRect /* 裁剪矩形 */ ); virtual ~Image(); - // 加载图片文件 + // 加载图片资源 bool open( - const String& filePath /* 图片文件路径 */ + const Resource& res ); // 加载图片资源 bool open( - int resNameId, /* 图片资源名称 */ - const String& resType /* 图片资源类型 */ + const String& fileName ); // 将图片裁剪为矩形 @@ -621,15 +656,9 @@ public: // 获取 ID2D1Bitmap 对象 ID2D1Bitmap * getBitmap(); - // 预加载图片文件 - static bool preload( - const String& filePath /* 图片文件路径 */ - ); - // 预加载图片资源 static bool preload( - int resNameId, /* 图片资源名称 */ - const String& resType /* 图片资源类型 */ + const Resource& res ); // 清空缓存 diff --git a/core/e2dnode.h b/core/e2dnode.h index 40a34a99..a3835750 100644 --- a/core/e2dnode.h +++ b/core/e2dnode.h @@ -452,22 +452,20 @@ public: ); explicit Sprite( - const String& filePath /* 图片文件路径 */ + const Resource& res ); explicit Sprite( - int resNameId, /* 图片资源名称 */ - const String& resType /* 图片资源类型 */ - ); - - explicit Sprite( - const String& filePath, /* 图片文件路径 */ + const Resource& res, const Rect& cropRect /* 裁剪矩形 */ ); explicit Sprite( - int resNameId, /* 图片资源名称 */ - const String& resType, /* 图片资源类型 */ + const String& fileName + ); + + explicit Sprite( + const String& fileName, const Rect& cropRect /* 裁剪矩形 */ ); @@ -475,13 +473,12 @@ public: // 加载图片文件 bool open( - const String& filePath + const Resource& res ); - // 加载图片资源 + // 加载图片文件 bool open( - int resNameId, /* 图片资源名称 */ - const String& resType /* 图片资源类型 */ + const String& fileName ); // 加载图片 diff --git a/core/e2dtool.h b/core/e2dtool.h index 6bf3fb13..b69c38ac 100644 --- a/core/e2dtool.h +++ b/core/e2dtool.h @@ -62,8 +62,7 @@ public: ); explicit Music( - int resNameId, /* 音乐资源名称 */ - const String& resType /* 音乐资源类型 */ + const Resource& res ); virtual ~Music(); @@ -75,8 +74,7 @@ public: // 打开音乐资源 bool open( - int resNameId, /* 音乐资源名称 */ - const String& resType /* 音乐资源类型 */ + const Resource& res ); // 播放 @@ -152,7 +150,7 @@ protected: class Player { friend class Game; - typedef std::map MusicMap; + typedef std::map MusicMap; public: // 获取播放器实例 @@ -194,39 +192,33 @@ public: // 预加载音乐资源 bool preload( - int resNameId, /* 音乐资源名称 */ - const String& resType /* 音乐资源类型 */ + const Resource& res /* 音乐资源 */ ); // 播放音乐 bool play( - int resNameId, /* 音乐资源名称 */ - const String& resType, /* 音乐资源类型 */ + const Resource& res, /* 音乐资源 */ int nLoopCount = 0 /* 重复播放次数,设置 -1 为循环播放 */ ); // 暂停音乐 void pause( - int resNameId, /* 音乐资源名称 */ - const String& resType /* 音乐资源类型 */ + const Resource& res /* 音乐资源 */ ); // 继续播放音乐 void resume( - int resNameId, /* 音乐资源名称 */ - const String& resType /* 音乐资源类型 */ + const Resource& res /* 音乐资源 */ ); // 停止音乐 void stop( - int resNameId, /* 音乐资源名称 */ - const String& resType /* 音乐资源类型 */ + const Resource& res /* 音乐资源 */ ); // 获取音乐播放状态 bool isPlaying( - int resNameId, /* 音乐资源名称 */ - const String& resType /* 音乐资源类型 */ + const Resource& res /* 音乐资源 */ ); // 获取音量 @@ -261,7 +253,6 @@ private: private: float _volume; - MusicMap _fileList; MusicMap _resList; IXAudio2* _xAudio2; IXAudio2MasteringVoice* _masteringVoice; @@ -504,29 +495,75 @@ public: }; -// 路径工具 -class Path +// 文件 +class File { - friend class Game; - public: - // 添加资源搜索路径 - static void addSearchPath( - String path + File(); + + File( + const String& fileName ); - // 检索文件路径 - static String findFile( - const String& path + virtual ~File(); + + // 打开文件 + bool open( + const String& fileName ); - // 提取资源文件,返回提取后的文件路径 - static String extractResource( + // 文件或文件夹是否存在 + bool exists() const; + + // 是否是文件夹 + bool isFolder() const; + + // 删除文件 + bool deleteFile(); + + // 获取文件路径 + String getFilePath() const; + + // 获取文件扩展名 + String getExtension() const; + + // 释放资源到临时文件目录 + static File extract( int resNameId, /* 资源名称 */ const String& resType, /* 资源类型 */ const String& destFileName /* 目标文件名 */ ); + // 添加文件搜索路径 + static void addSearchPath( + const String& path + ); + + // 创建文件夹 + static bool createFolder( + const String& dirPath /* 文件夹路径 */ + ); + + // 打开保存文件对话框 + static String getSaveFilePath( + const String& title = L"保存到", /* 对话框标题 */ + const String& defExt = L"" /* 默认扩展名 */ + ); + +protected: + DWORD _attributes; + String _fileName; + + static std::list _searchPaths; +}; + + +// 路径 +class Path +{ + friend class Game; + +public: // 获取数据的默认保存路径 static String getDataPath(); @@ -538,32 +575,6 @@ public: // 获取当前程序的运行路径 static String getCurrentFilePath(); - - // 获取文件扩展名 - static String getFileExtension( - const String& filePath - ); - - // 打开保存文件对话框 - static String getSaveFilePath( - const String& title = L"保存到", /* 对话框标题 */ - const String& defExt = L"" /* 默认扩展名 */ - ); - - // 创建文件夹 - static bool createFolder( - const String& dirPath /* 文件夹路径 */ - ); - - // 判断文件或文件夹是否存在 - static bool exists( - const String& dirPath /* 文件夹路径 */ - ); - -private: - static String _tempPath; - static String _dataPath; - static std::list _paths; }; } \ No newline at end of file diff --git a/project/vs2017/Easy2D.vcxproj b/project/vs2017/Easy2D.vcxproj index 10f9a185..e5c5eb36 100644 --- a/project/vs2017/Easy2D.vcxproj +++ b/project/vs2017/Easy2D.vcxproj @@ -230,6 +230,7 @@ + @@ -253,6 +254,7 @@ + diff --git a/project/vs2017/Easy2D.vcxproj.filters b/project/vs2017/Easy2D.vcxproj.filters index e610a2a0..d4c6d836 100644 --- a/project/vs2017/Easy2D.vcxproj.filters +++ b/project/vs2017/Easy2D.vcxproj.filters @@ -240,6 +240,12 @@ Common + + Tool + + + Common +