/**************************************************************************** Copyright (c) 2007, Radon Labs GmbH Copyright (c) 2011-2013,WebJet Business Division,CYOU http://www.genesis-3d.com.cn Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ****************************************************************************/ #if WIN32 // - only need this headers #include #include #include #include // for _InterlockedIncrement #include #include // for sinf, cosf,etc //#include "stdneb.h" // - #define NOCTLMGR in precompiled.h make a conflict with shlobj.h, so comment it #include "io/win360/win360fswrapper.h" //#include "core/sysfunc.h" // - do not need so much headers include in this file #if __WIN32__ #include "util/win32/win32stringconverter.h" #endif namespace Win360 { using namespace Util; using namespace Core; using namespace IO; //------------------------------------------------------------------------------ /** Open a file using the Xbox360 function CreateFile(). Returns a handle to the file which must be passed to the other Win360FSWrapper file methods. If opening the file fails, the function will return 0. The filename must be a native Xbox360 path (no assigns, etc...). */ Win360FSWrapper::Handle Win360FSWrapper::OpenFile(const String& path, Stream::AccessMode accessMode, Stream::AccessPattern accessPattern, DWORD flagsAndAttributes) { #if __XBOX360__ String xbox360Path = path; xbox360Path.SubstituteChar('/', '\\'); #else ushort widePath[1024]; Win32::Win32StringConverter::UTF8ToWide(path, widePath, sizeof(widePath)); #endif DWORD access = 0; DWORD disposition = 0; DWORD shareMode = 0; switch (accessMode) { case Stream::ReadAccess: access = GENERIC_READ; disposition = OPEN_EXISTING; shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; break; case Stream::WriteAccess: access = GENERIC_WRITE; disposition = CREATE_ALWAYS; shareMode = FILE_SHARE_READ; break; case Stream::ReadWriteAccess: case Stream::AppendAccess: access = GENERIC_READ | GENERIC_WRITE; disposition = OPEN_ALWAYS; shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; break; } switch (accessPattern) { case Stream::Random: flagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS; break; case Stream::Sequential: flagsAndAttributes |= FILE_FLAG_SEQUENTIAL_SCAN; break; } // open/create the file #if __XBOX360__ Handle handle = CreateFile(xbox360Path.AsCharPtr(), // lpFileName access, // dwDesiredAccess shareMode, // dwShareMode 0, // lpSecurityAttributes disposition, // dwCreationDisposition, flagsAndAttributes, // dwFlagsAndAttributes NULL); // hTemplateFile #else Handle handle = CreateFileW((LPCWSTR)widePath, // lpFileName access, // dwDesiredAccess shareMode, // dwShareMode 0, // lpSecurityAttributes disposition, // dwCreationDisposition, flagsAndAttributes, // dwFlagsAndAttributes NULL); // hTemplateFile #endif if (handle != INVALID_HANDLE_VALUE) { // in append mode, we need to seek to the end of the file if (Stream::AppendAccess == accessMode) { SetFilePointer(handle, 0, NULL, FILE_END); } return handle; } else { return 0; } } //------------------------------------------------------------------------------ /** Closes a file opened by Win360FSWrapper::OpenFile(). */ void Win360FSWrapper::CloseFile(Handle handle) { n_assert(0 != handle); CloseHandle(handle); } //------------------------------------------------------------------------------ /** Write data to a file. */ void Win360FSWrapper::Write(Handle handle, const void* buf, Stream::Size numBytes) { n_assert(0 != handle); n_assert(buf != 0); n_assert(numBytes > 0); DWORD bytesWritten; BOOL result = WriteFile(handle, buf, numBytes, &bytesWritten, NULL); if ((0 == result) || ((DWORD)numBytes != bytesWritten)) { DWORD error = ::GetLastError(); n_error("Win360FSWrapper: WriteFile() failed! %d", error); } } //------------------------------------------------------------------------------ /** Read data from a file, returns number of bytes read. */ Stream::Size Win360FSWrapper::Read(Handle handle, void* buf, Stream::Size numBytes) { n_assert(0 != handle); n_assert(buf != 0); n_assert(numBytes > 0); DWORD bytesRead; BOOL result = ReadFile(handle, buf, numBytes, &bytesRead, NULL); if (0 == result) { n_error("Win360FSWrapper: ReadFile() failed!"); } return bytesRead; } //------------------------------------------------------------------------------ /** Seek in a file. */ void Win360FSWrapper::Seek(Handle handle, Stream::Offset offset, Stream::SeekOrigin orig) { n_assert(0 != handle); DWORD moveMethod; switch (orig) { case Stream::Begin: moveMethod = FILE_BEGIN; break; case Stream::Current: moveMethod = FILE_CURRENT; break; case Stream::End: moveMethod = FILE_END; break; default: // can't happen moveMethod = FILE_BEGIN; break; } SetFilePointer(handle, offset, NULL, moveMethod); } //------------------------------------------------------------------------------ /** Get current position in file. */ Stream::Position Win360FSWrapper::Tell(Handle handle) { n_assert(0 != handle); return SetFilePointer(handle, 0, NULL, FILE_CURRENT); } //------------------------------------------------------------------------------ /** Flush unwritten data to file. */ void Win360FSWrapper::Flush(Handle handle) { n_assert(0 != handle); FlushFileBuffers(handle); } //------------------------------------------------------------------------------ /** Returns true if current position is at end of file. */ bool Win360FSWrapper::Eof(Handle handle) { n_assert(0 != handle); DWORD fpos = SetFilePointer(handle, 0, NULL, FILE_CURRENT); DWORD size = ::GetFileSize(handle, NULL); // NOTE: THE '>=' IS NOT A BUG!!! return fpos >= size; } //------------------------------------------------------------------------------ /** Returns the size of a file in bytes. */ Stream::Size Win360FSWrapper::GetFileSize(Handle handle) { n_assert(0 != handle); return ::GetFileSize(handle, NULL); } //------------------------------------------------------------------------------ /** Set the read-only status of a file. This method does nothing on the Xbox360. */ void Win360FSWrapper::SetReadOnly(const String& path, bool readOnly) { #if __WIN32__ n_assert(path.IsValid()); ushort widePath[1024]; Win32::Win32StringConverter::UTF8ToWide(path, widePath, sizeof(widePath)); DWORD fileAttrs = GetFileAttributesW((LPCWSTR)widePath); if (readOnly) { fileAttrs |= FILE_ATTRIBUTE_READONLY; } else { fileAttrs &= ~FILE_ATTRIBUTE_READONLY; } SetFileAttributes(path.AsCharPtr(), fileAttrs); #else // NOT AVAILABLE ON XBOX360 #endif } //------------------------------------------------------------------------------ /** Get the read-only status of a file. This method always returns true on the Xbox360. */ bool Win360FSWrapper::IsReadOnly(const String& path) { #if __WIN32__ n_assert(path.IsValid()); ushort widePath[1024]; Win32::Win32StringConverter::UTF8ToWide(path, widePath, sizeof(widePath)); DWORD fileAttrs = GetFileAttributesW((LPCWSTR)widePath); return (fileAttrs & FILE_ATTRIBUTE_READONLY); #else // always read-only on the 360 return true; #endif } //------------------------------------------------------------------------------ /** Deletes a file. Returns true if the operation was successful. The delete will fail if the fail doesn't exist or the file is read-only. */ bool Win360FSWrapper::DeleteFile(const String& path) { n_assert(path.IsValid()); #if __XBOX360__ String nativePath = path; nativePath.SubstituteChar('/', '\\'); return (0 != ::DeleteFileA(nativePath.AsCharPtr())); #else ushort widePath[1024]; Win32::Win32StringConverter::UTF8ToWide(path, widePath, sizeof(widePath)); return (0 != ::DeleteFileW((LPCWSTR)widePath)); #endif } //------------------------------------------------------------------------------ /** Delete an empty directory. Returns true if the operation was successful. */ bool Win360FSWrapper::DeleteDirectory(const String& path) { n_assert(path.IsValid()); #if __XBOX360__ String nativePath = path; nativePath.SubstituteChar('/', '\\'); return (0 != ::RemoveDirectoryA(nativePath.AsCharPtr())); #else ushort widePath[1024]; Win32::Win32StringConverter::UTF8ToWide(path, widePath, sizeof(widePath)); return (0 != ::RemoveDirectoryW((LPCWSTR)widePath)); #endif } //------------------------------------------------------------------------------ /** Return true if a file exists. */ bool Win360FSWrapper::FileExists(const String& path) { n_assert(path.IsValid()); #if __XBOX360__ String nativePath = path; nativePath.SubstituteChar('/', '\\'); DWORD fileAttrs = GetFileAttributesA(nativePath.AsCharPtr()); #else ushort widePath[1024]; Win32::Win32StringConverter::UTF8ToWide(path, widePath, sizeof(widePath)); DWORD fileAttrs = GetFileAttributesW((LPCWSTR)widePath); #endif if ((-1 != fileAttrs) && (0 == (FILE_ATTRIBUTE_DIRECTORY & fileAttrs))) { return true; } else { return false; } } //------------------------------------------------------------------------------ /** Return true if a directory exists. */ bool Win360FSWrapper::DirectoryExists(const String& path) { n_assert(path.IsValid()); #if __XBOX360__ String nativePath = path; nativePath.SubstituteChar('/', '\\'); DWORD fileAttrs = GetFileAttributesA(nativePath.AsCharPtr()); #else ushort widePath[1024]; Win32::Win32StringConverter::UTF8ToWide(path, widePath, sizeof(widePath)); DWORD fileAttrs = GetFileAttributesW((LPCWSTR)widePath); #endif if ((-1 != fileAttrs) && (0 != (FILE_ATTRIBUTE_DIRECTORY & fileAttrs))) { return true; } else { return false; } } //------------------------------------------------------------------------------ /** Set the write-access time stamp of a file. */ void Win360FSWrapper::SetFileWriteTime(const String& path, FileTime fileTime) { n_assert(path.IsValid()); Handle h = Win360FSWrapper::OpenFile(path, Stream::ReadWriteAccess, Stream::Sequential); if (0 != h) { SetFileTime(h, NULL, NULL, &fileTime.time); Win360FSWrapper::CloseFile(h); } else { n_error("Win360FSWrapper::SetFileWriteTime(): failed to open file '%s'!", path.AsCharPtr()); } } //------------------------------------------------------------------------------ /** Return the last write-access time to a file. */ FileTime Win360FSWrapper::GetFileWriteTime(const String& path) { n_assert(path.IsValid()); FileTime fileTime; Handle h = Win360FSWrapper::OpenFile(path, Stream::ReadAccess, Stream::Sequential); if (0 != h) { GetFileTime(h, NULL, NULL, &fileTime.time); Win360FSWrapper::CloseFile(h); } else { // do not fail hard if file does not exist // n_printf("Win360FSWrapper::GetFileWriteTime(): failed to open file '%s'!", path.AsCharPtr()); } return fileTime; } //------------------------------------------------------------------------------ /** Creates a new directory. */ bool Win360FSWrapper::CreateDirectory(const String& path) { n_assert(path.IsValid()); #if __XBOX360__ String nativePath = path; nativePath.SubstituteChar('/', '\\'); return (0 != ::CreateDirectoryA(nativePath.AsCharPtr(), NULL)); #else const String& nativePath = path; ushort widePath[1024]; Win32::Win32StringConverter::UTF8ToWide(path, widePath, sizeof(widePath)); return (0 != ::CreateDirectoryW((LPCWSTR)widePath, NULL)); #endif } //------------------------------------------------------------------------------ /** Lists all files in a directory, filtered by a pattern. */ Array Win360FSWrapper::ListFiles(const String& dirPath, const String& pattern) { n_assert(dirPath.IsValid()); n_assert(pattern.IsValid()); String pathWithPattern = dirPath + "/" + pattern; #if __XBOX360__ pathWithPattern.SubstituteChar('/', '\\'); #else ushort widePath[1024]; Win32::Win32StringConverter::UTF8ToWide(pathWithPattern, widePath, sizeof(widePath)); #endif Array result; HANDLE hFind; #if __XBOX360__ WIN32_FIND_DATA findFileData; hFind = FindFirstFileA(pathWithPattern.AsCharPtr(), &findFileData); if (INVALID_HANDLE_VALUE != hFind) { do { if (0 == (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { result.Append(findFileData.cFileName); } } while (FindNextFile(hFind, &findFileData) != 0); FindClose(hFind); } #else WIN32_FIND_DATAW findFileData; hFind = FindFirstFileW((LPCWSTR)widePath, &findFileData); if (INVALID_HANDLE_VALUE != hFind) { do { if (0 == (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { result.Append(Win32::Win32StringConverter::WideToUTF8((ushort*)findFileData.cFileName)); } } while (FindNextFileW(hFind, &findFileData) != 0); FindClose(hFind); } #endif return result; } //------------------------------------------------------------------------------ /** Lists all subdirectories in a directory, filtered by a pattern. This will not return the special directories ".." and ".". */ Array Win360FSWrapper::ListDirectories(const String& dirPath, const String& pattern) { n_assert(dirPath.IsValid()); n_assert(pattern.IsValid()); String pathWithPattern = dirPath + "/" + pattern; #if __XBOX360__ pathWithPattern.SubstituteChar('/', '\\'); #else ushort widePath[1024]; Win32::Win32StringConverter::UTF8ToWide(pathWithPattern, widePath, sizeof(widePath)); #endif Array result; HANDLE hFind; #if __XBOX360__ WIN32_FIND_DATA findFileData; hFind = FindFirstFile(pathWithPattern.AsCharPtr(), &findFileData); if (INVALID_HANDLE_VALUE != hFind) { do { String fileName = findFileData.cFileName; if ((0 != (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) && (fileName != "..") && (fileName != ".")) { result.Append(findFileData.cFileName); } } while (FindNextFile(hFind, &findFileData) != 0); FindClose(hFind); } #else WIN32_FIND_DATAW findFileData; hFind = FindFirstFileW((LPCWSTR)widePath, &findFileData); if (INVALID_HANDLE_VALUE != hFind) { do { String fileName = Win32::Win32StringConverter::WideToUTF8((ushort*)findFileData.cFileName); if ((0 != (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) && (fileName != "..") && (fileName != ".")) { result.Append(fileName); } } while (FindNextFileW(hFind, &findFileData) != 0); FindClose(hFind); } #endif return result; } //------------------------------------------------------------------------------ /** NOTE: The user: standard assign is not supported on the 360. */ String Win360FSWrapper::GetUserDirectory() { #if __WIN32__ ushort wideBuffer[NEBULA3_MAXPATH] = { 0 }; HRESULT hr = SHGetFolderPathW(NULL, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, 0, (LPWSTR)wideBuffer); n_assert(SUCCEEDED(hr)); String result = Win32::Win32StringConverter::WideToUTF8(wideBuffer); result.ConvertBackslashes(); return String("file:///") + result; #else // there is no user: assign on the 360 return ""; #endif } //------------------------------------------------------------------------------ /** NOTE: The appdata: standard assign is not supported on the 360. */ String Win360FSWrapper::GetAppDataDirectory() { #if __WIN32__ ushort wideBuffer[NEBULA3_MAXPATH] = { 0 }; HRESULT hr = SHGetFolderPathW(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, (LPWSTR)wideBuffer); n_assert(SUCCEEDED(hr)); String result = Win32::Win32StringConverter::WideToUTF8(wideBuffer); result.ConvertBackslashes(); return String("file:///") + result; #else // there is no user: assign on the 360 return ""; #endif } //------------------------------------------------------------------------------ /** NOTE: The programs: standard assign is not supported on the 360. */ String Win360FSWrapper::GetProgramsDirectory() { #if __WIN32__ ushort wideBuffer[NEBULA3_MAXPATH] = { 0 }; HRESULT hr = SHGetFolderPathW(NULL, CSIDL_PROGRAM_FILES, NULL, 0, (LPWSTR)wideBuffer); n_assert(SUCCEEDED(hr)); String result = Win32::Win32StringConverter::WideToUTF8(wideBuffer); result.ConvertBackslashes(); return String("file:///") + result; #else // there is no programs: assign on the 360 return ""; #endif } //------------------------------------------------------------------------------ /** NOTE: The temp standard assign is not supported on the 360 (only on Devkits!) */ String Win360FSWrapper::GetTempDirectory() { #if __WIN32__ ushort wideBuffer[NEBULA3_MAXPATH] = { 0 }; GetTempPathW(sizeof(wideBuffer) / 2, (LPWSTR)wideBuffer); String result = Win32::Win32StringConverter::WideToUTF8(wideBuffer); result.ConvertBackslashes(); result.TrimRight("/"); return String("file:///") + result; #else // @todo: CAREFUL, THIS ONLY EXISTS ON A XBOX360 DEVKIT! return "file:///DEVKIT:"; #endif } //------------------------------------------------------------------------------ /** This method sould return the directory where the application executable is located. */ String Win360FSWrapper::GetBinDirectory() { #if __WIN32__ ushort wideBuffer[NEBULA3_MAXPATH]; DWORD res = GetModuleFileNameW(NULL, (LPWSTR)wideBuffer, sizeof(wideBuffer) / 2); n_assert(0 != res); String result = Win32::Win32StringConverter::WideToUTF8(wideBuffer); result.ConvertBackslashes(); result = result.ExtractDirName(); result.TrimRight("/"); return String("file:///") + result; #else return "file:///GAME:/"; #endif } //------------------------------------------------------------------------------ /** This method should return the installation directory of the application. */ String Win360FSWrapper::GetHomeDirectory() { #if __WIN32__ ushort wideBuffer[NEBULA3_MAXPATH]; DWORD res = GetModuleFileNameW(NULL, (LPWSTR)wideBuffer, sizeof(wideBuffer) / 2); n_assert(0 != res); String pathToExe = Win32::Win32StringConverter::WideToUTF8(wideBuffer); pathToExe.ConvertBackslashes(); // check if executable resides in a win32 directory String pathToDir = pathToExe.ExtractLastDirName(); if (n_stricmp(pathToDir.AsCharPtr(), "win32") == 0) { // normal home:bin/win32 directory structure // strip bin/win32 String homePath = pathToExe.ExtractDirName(); homePath = homePath.ExtractDirName(); homePath = homePath.ExtractDirName(); homePath.TrimRight("/"); return String("file:///") + homePath; } else { // not in normal home:bin/win32 directory structure, // use the exe's directory as home path String homePath = pathToExe.ExtractDirName(); return String("file:///") + homePath; } #else // Xbox360 case is a bit simpler... return "file:///GAME:/"; #endif } //------------------------------------------------------------------------------ /** Return true if the provided string is a device name. */ bool Win360FSWrapper::IsDeviceName(const Util::String& str) { #if __WIN32__ if (str.Length() == 1) { uchar c = str[0]; if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))) { return true; } } return false; #else // on Xbox360: if (str == "GAME") return true; else if (str == "DEVKIT") return true; else return false; #endif } //------------------------------------------------------------------------------ /** This method should return the path of the editor asset directory. */ String Win360FSWrapper::GetEditorAssetDirectory() { #if __WIN32__ && __GENESIS_EDITOR__ ushort wideBuffer[NEBULA3_MAXPATH]; DWORD res = GetModuleFileNameW(NULL, (LPWSTR)wideBuffer, sizeof(wideBuffer) / 2); n_assert(0 != res); String result = Win32::Win32StringConverter::WideToUTF8(wideBuffer); result.ConvertBackslashes(); result = result.ExtractDirName(); String editorPath = result.ExtractDirName(); editorPath += "editorasset/"; if (DirectoryExists(editorPath)) { editorPath.TrimRight("/"); result = editorPath; } else { n_error("Editor asset folder not found!"); } return String("file:///") + result; #else return "file:///GAME:/"; #endif } } // namespace IO #endif