diff options
| author | Mat M <mathew1800@gmail.com> | 2017-01-31 22:28:42 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-01-31 22:28:42 -0500 |
| commit | 6fd0e96ea3992e62f6750fcebfeec4755d110435 (patch) | |
| tree | 8399f62d91554c1b8603ddf0f42d29f36e90fe82 /Source/Core | |
| parent | 84d81a4b7a9622e00c723fa8aec648f7b71e2717 (diff) | |
| parent | d2da1897e7943fd6f8551528778219e0d2a5acd8 (diff) | |
Merge pull request #4785 from lioncash/ios-fs
IOS FS: Move behavior to separate functions
Diffstat (limited to 'Source/Core')
| -rw-r--r-- | Source/Core/Core/IOS/FS/FS.cpp | 950 | ||||
| -rw-r--r-- | Source/Core/Core/IOS/FS/FS.h | 13 |
2 files changed, 494 insertions, 469 deletions
diff --git a/Source/Core/Core/IOS/FS/FS.cpp b/Source/Core/Core/IOS/FS/FS.cpp index cebd8dfc5c..912b475f6a 100644 --- a/Source/Core/Core/IOS/FS/FS.cpp +++ b/Source/Core/Core/IOS/FS/FS.cpp @@ -37,11 +37,99 @@ FS::FS(u32 device_id, const std::string& device_name) : Device(device_id, device { } -// ~1/1000th of a second is too short and causes hangs in Wii Party -// Play it safe at 1/500th -IPCCommandResult FS::GetFSReply(const s32 return_value) const +void FS::DoState(PointerWrap& p) { - return {return_value, true, SystemTimers::GetTicksPerSecond() / 500}; + DoStateShared(p); + + // handle /tmp + + std::string Path = File::GetUserPath(D_SESSION_WIIROOT_IDX) + "/tmp"; + if (p.GetMode() == PointerWrap::MODE_READ) + { + File::DeleteDirRecursively(Path); + File::CreateDir(Path); + + // now restore from the stream + while (1) + { + char type = 0; + p.Do(type); + if (!type) + break; + std::string filename; + p.Do(filename); + std::string name = Path + DIR_SEP + filename; + switch (type) + { + case 'd': + { + File::CreateDir(name); + break; + } + case 'f': + { + u32 size = 0; + p.Do(size); + + File::IOFile handle(name, "wb"); + char buf[65536]; + u32 count = size; + while (count > 65536) + { + p.DoArray(buf); + handle.WriteArray(&buf[0], 65536); + count -= 65536; + } + p.DoArray(&buf[0], count); + handle.WriteArray(&buf[0], count); + break; + } + } + } + } + else + { + // recurse through tmp and save dirs and files + + File::FSTEntry parentEntry = File::ScanDirectoryTree(Path, true); + std::deque<File::FSTEntry> todo; + todo.insert(todo.end(), parentEntry.children.begin(), parentEntry.children.end()); + + while (!todo.empty()) + { + File::FSTEntry& entry = todo.front(); + std::string name = entry.physicalName; + name.erase(0, Path.length() + 1); + char type = entry.isDirectory ? 'd' : 'f'; + p.Do(type); + p.Do(name); + if (entry.isDirectory) + { + todo.insert(todo.end(), entry.children.begin(), entry.children.end()); + } + else + { + u32 size = (u32)entry.size; + p.Do(size); + + File::IOFile handle(entry.physicalName, "rb"); + char buf[65536]; + u32 count = size; + while (count > 65536) + { + handle.ReadArray(&buf[0], 65536); + p.DoArray(buf); + count -= 65536; + } + handle.ReadArray(&buf[0], count); + p.DoArray(&buf[0], count); + } + todo.pop_front(); + } + + char type = 0; + p.Do(type); + } } ReturnCode FS::Open(const OpenRequest& request) @@ -72,571 +160,497 @@ static u64 ComputeTotalFileSize(const File::FSTEntry& parentEntry) return sizeOfFiles; } +IPCCommandResult FS::IOCtl(const IOCtlRequest& request) +{ + Memory::Memset(request.buffer_out, 0, request.buffer_out_size); + + switch (request.request) + { + case IOCTL_GET_STATS: + return GetStats(request); + case IOCTL_CREATE_DIR: + return CreateDirectory(request); + case IOCTL_SET_ATTR: + return SetAttribute(request); + case IOCTL_GET_ATTR: + return GetAttribute(request); + case IOCTL_DELETE_FILE: + return DeleteFile(request); + case IOCTL_RENAME_FILE: + return RenameFile(request); + case IOCTL_CREATE_FILE: + return CreateFile(request); + case IOCTL_SHUTDOWN: + return Shutdown(request); + default: + request.DumpUnknown(GetDeviceName(), LogTypes::IOS_FILEIO); + break; + } + + return GetFSReply(FS_EINVAL); +} + IPCCommandResult FS::IOCtlV(const IOCtlVRequest& request) { - s32 return_value = IPC_SUCCESS; switch (request.request) { case IOCTLV_READ_DIR: - { - const std::string relative_path = - Memory::GetString(request.in_vectors[0].address, request.in_vectors[0].size); + return ReadDirectory(request); + case IOCTLV_GETUSAGE: + return GetUsage(request); + default: + request.DumpUnknown(GetDeviceName(), LogTypes::IOS_FILEIO); + break; + } - if (!IsValidWiiPath(relative_path)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", relative_path.c_str()); - return_value = FS_EINVAL; - break; - } + return GetFSReply(IPC_SUCCESS); +} - // the Wii uses this function to define the type (dir or file) - std::string DirName(HLE_IPC_BuildFilename(relative_path)); +// ~1/1000th of a second is too short and causes hangs in Wii Party +// Play it safe at 1/500th +IPCCommandResult FS::GetFSReply(const s32 return_value) const +{ + return {return_value, true, SystemTimers::GetTicksPerSecond() / 500}; +} - INFO_LOG(IOS_FILEIO, "FS: IOCTL_READ_DIR %s", DirName.c_str()); +IPCCommandResult FS::GetStats(const IOCtlRequest& request) +{ + if (request.buffer_out_size < 0x1c) + return GetFSReply(-1017); - if (!File::Exists(DirName)) - { - WARN_LOG(IOS_FILEIO, "FS: Search not found: %s", DirName.c_str()); - return_value = FS_ENOENT; - break; - } - else if (!File::IsDirectory(DirName)) - { - // It's not a directory, so error. - // Games don't usually seem to care WHICH error they get, as long as it's < - // Well the system menu CARES! - WARN_LOG(IOS_FILEIO, "\tNot a directory - return FS_EINVAL"); - return_value = FS_EINVAL; - break; - } + WARN_LOG(IOS_FILEIO, "FS: GET STATS - returning static values for now"); - File::FSTEntry entry = File::ScanDirectoryTree(DirName, false); + // TODO: scrape the real amounts from somewhere... + NANDStat fs; + fs.BlockSize = 0x4000; + fs.FreeUserBlocks = 0x5DEC; + fs.UsedUserBlocks = 0x1DD4; + fs.FreeSysBlocks = 0x10; + fs.UsedSysBlocks = 0x02F0; + fs.Free_INodes = 0x146B; + fs.Used_Inodes = 0x0394; - // it is one - if ((request.in_vectors.size() == 1) && (request.io_vectors.size() == 1)) - { - size_t numFile = entry.children.size(); - INFO_LOG(IOS_FILEIO, "\t%zu files found", numFile); + std::memcpy(Memory::GetPointer(request.buffer_out), &fs, sizeof(NANDStat)); - Memory::Write_U32((u32)numFile, request.io_vectors[0].address); - } - else - { - for (File::FSTEntry& child : entry.children) - { - // Decode escaped invalid file system characters so that games (such as - // Harry Potter and the Half-Blood Prince) can find what they expect. - child.virtualName = Common::UnescapeFileName(child.virtualName); - } + return GetFSReply(IPC_SUCCESS); +} - std::sort(entry.children.begin(), entry.children.end(), - [](const File::FSTEntry& one, const File::FSTEntry& two) { - return one.virtualName < two.virtualName; - }); +IPCCommandResult FS::CreateDirectory(const IOCtlRequest& request) +{ + _dbg_assert_(IOS_FILEIO, request.buffer_out_size == 0); + u32 Addr = request.buffer_in; - u32 MaxEntries = Memory::Read_U32(request.in_vectors[0].address); + u32 OwnerID = Memory::Read_U32(Addr); + Addr += 4; + u16 GroupID = Memory::Read_U16(Addr); + Addr += 2; - memset(Memory::GetPointer(request.io_vectors[0].address), 0, request.io_vectors[0].size); + const std::string wii_path = Memory::GetString(Addr, 64); + if (!IsValidWiiPath(wii_path)) + { + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); + return GetFSReply(FS_EINVAL); + } - size_t numFiles = 0; - char* pFilename = (char*)Memory::GetPointer((u32)(request.io_vectors[0].address)); + std::string DirName(HLE_IPC_BuildFilename(wii_path)); + Addr += 64; + Addr += 9; // owner attribs, permission + u8 Attribs = Memory::Read_U8(Addr); - for (size_t i = 0; i < entry.children.size() && i < MaxEntries; i++) - { - const std::string& FileName = entry.children[i].virtualName; + INFO_LOG(IOS_FILEIO, "FS: CREATE_DIR %s, OwnerID %#x, GroupID %#x, Attributes %#x", + DirName.c_str(), OwnerID, GroupID, Attribs); - strcpy(pFilename, FileName.c_str()); - pFilename += FileName.length(); - *pFilename++ = 0x00; // termination - numFiles++; + DirName += DIR_SEP; + File::CreateFullPath(DirName); + _dbg_assert_msg_(IOS_FILEIO, File::IsDirectory(DirName), "FS: CREATE_DIR %s failed", + DirName.c_str()); - INFO_LOG(IOS_FILEIO, "\tFound: %s", FileName.c_str()); - } + return GetFSReply(IPC_SUCCESS); +} - Memory::Write_U32((u32)numFiles, request.io_vectors[1].address); - } +IPCCommandResult FS::SetAttribute(const IOCtlRequest& request) +{ + u32 Addr = request.buffer_in; - return_value = IPC_SUCCESS; - } - break; + u32 OwnerID = Memory::Read_U32(Addr); + Addr += 4; + u16 GroupID = Memory::Read_U16(Addr); + Addr += 2; - case IOCTLV_GETUSAGE: + const std::string wii_path = Memory::GetString(Addr, 64); + if (!IsValidWiiPath(wii_path)) { - _dbg_assert_(IOS_FILEIO, request.io_vectors.size() == 2); - _dbg_assert_(IOS_FILEIO, request.io_vectors[0].size == 4); - _dbg_assert_(IOS_FILEIO, request.io_vectors[1].size == 4); + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); + return GetFSReply(FS_EINVAL); + } - // this command sucks because it asks of the number of used - // fsBlocks and inodes - // It should be correct, but don't count on it... - std::string relativepath = - Memory::GetString(request.in_vectors[0].address, request.in_vectors[0].size); + std::string Filename = HLE_IPC_BuildFilename(wii_path); + Addr += 64; + u8 OwnerPerm = Memory::Read_U8(Addr); + Addr += 1; + u8 GroupPerm = Memory::Read_U8(Addr); + Addr += 1; + u8 OtherPerm = Memory::Read_U8(Addr); + Addr += 1; + u8 Attributes = Memory::Read_U8(Addr); + Addr += 1; + + INFO_LOG(IOS_FILEIO, "FS: SetAttrib %s", Filename.c_str()); + DEBUG_LOG(IOS_FILEIO, " OwnerID: 0x%08x", OwnerID); + DEBUG_LOG(IOS_FILEIO, " GroupID: 0x%04x", GroupID); + DEBUG_LOG(IOS_FILEIO, " OwnerPerm: 0x%02x", OwnerPerm); + DEBUG_LOG(IOS_FILEIO, " GroupPerm: 0x%02x", GroupPerm); + DEBUG_LOG(IOS_FILEIO, " OtherPerm: 0x%02x", OtherPerm); + DEBUG_LOG(IOS_FILEIO, " Attributes: 0x%02x", Attributes); + + return GetFSReply(IPC_SUCCESS); +} - if (!IsValidWiiPath(relativepath)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", relativepath.c_str()); - return_value = FS_EINVAL; - break; - } +IPCCommandResult FS::GetAttribute(const IOCtlRequest& request) +{ + _dbg_assert_msg_(IOS_FILEIO, request.buffer_out_size == 76, + " GET_ATTR needs an 76 bytes large output buffer but it is %i bytes large", + request.buffer_out_size); - std::string path(HLE_IPC_BuildFilename(relativepath)); - u32 fsBlocks = 0; - u32 iNodes = 0; + u32 OwnerID = 0; + u16 GroupID = 0x3031; // this is also known as makercd, 01 (0x3031) for nintendo and 08 + // (0x3038) for MH3 etc - INFO_LOG(IOS_FILEIO, "IOCTL_GETUSAGE %s", path.c_str()); - if (File::IsDirectory(path)) - { - // LPFaint99: After I found that setting the number of inodes to the number of children + 1 - // for the directory itself - // I decided to compare with sneek which has the following 2 special cases which are - // Copyright (C) 2009-2011 crediar http://code.google.com/p/sneek/ - if ((relativepath.compare(0, 16, "/title/00010001") == 0) || - (relativepath.compare(0, 16, "/title/00010005") == 0)) - { - fsBlocks = 23; // size is size/0x4000 - iNodes = 42; // empty folders return a FileCount of 1 - } - else - { - File::FSTEntry parentDir = File::ScanDirectoryTree(path, true); - // add one for the folder itself - iNodes = 1 + (u32)parentDir.size; - - u64 totalSize = - ComputeTotalFileSize(parentDir); // "Real" size, to be converted to nand blocks + const std::string wii_path = Memory::GetString(request.buffer_in, 64); + if (!IsValidWiiPath(wii_path)) + { + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); + return GetFSReply(FS_EINVAL); + } - fsBlocks = (u32)(totalSize / (16 * 1024)); // one bock is 16kb - } - return_value = IPC_SUCCESS; + std::string Filename = HLE_IPC_BuildFilename(wii_path); + u8 OwnerPerm = 0x3; // read/write + u8 GroupPerm = 0x3; // read/write + u8 OtherPerm = 0x3; // read/write + u8 Attributes = 0x00; // no attributes - INFO_LOG(IOS_FILEIO, "FS: fsBlock: %i, iNodes: %i", fsBlocks, iNodes); + if (File::IsDirectory(Filename)) + { + INFO_LOG(IOS_FILEIO, "FS: GET_ATTR Directory %s - all permission flags are set", + Filename.c_str()); + } + else + { + if (File::Exists(Filename)) + { + INFO_LOG(IOS_FILEIO, "FS: GET_ATTR %s - all permission flags are set", Filename.c_str()); } else { - fsBlocks = 0; - iNodes = 0; - return_value = IPC_SUCCESS; - WARN_LOG(IOS_FILEIO, "FS: fsBlock failed, cannot find directory: %s", path.c_str()); + INFO_LOG(IOS_FILEIO, "FS: GET_ATTR unknown %s", Filename.c_str()); + return GetFSReply(FS_ENOENT); } - - Memory::Write_U32(fsBlocks, request.io_vectors[0].address); - Memory::Write_U32(iNodes, request.io_vectors[1].address); } - break; - default: - request.DumpUnknown(GetDeviceName(), LogTypes::IOS_FILEIO); - break; + // write answer to buffer + if (request.buffer_out_size == 76) + { + u32 Addr = request.buffer_out; + Memory::Write_U32(OwnerID, Addr); + Addr += 4; + Memory::Write_U16(GroupID, Addr); + Addr += 2; + memcpy(Memory::GetPointer(Addr), Memory::GetPointer(request.buffer_in), 64); + Addr += 64; + Memory::Write_U8(OwnerPerm, Addr); + Addr += 1; + Memory::Write_U8(GroupPerm, Addr); + Addr += 1; + Memory::Write_U8(OtherPerm, Addr); + Addr += 1; + Memory::Write_U8(Attributes, Addr); + Addr += 1; } - return GetFSReply(return_value); + return GetFSReply(IPC_SUCCESS); } -IPCCommandResult FS::IOCtl(const IOCtlRequest& request) +IPCCommandResult FS::DeleteFile(const IOCtlRequest& request) { - Memory::Memset(request.buffer_out, 0, request.buffer_out_size); - const s32 return_value = ExecuteCommand(request); - return GetFSReply(return_value); -} + _dbg_assert_(IOS_FILEIO, request.buffer_out_size == 0); + int Offset = 0; -s32 FS::ExecuteCommand(const IOCtlRequest& request) -{ - switch (request.request) + const std::string wii_path = Memory::GetString(request.buffer_in + Offset, 64); + if (!IsValidWiiPath(wii_path)) { - case IOCTL_GET_STATS: - { - if (request.buffer_out_size < 0x1c) - return -1017; - - WARN_LOG(IOS_FILEIO, "FS: GET STATS - returning static values for now"); + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); + return GetFSReply(FS_EINVAL); + } - NANDStat fs; + std::string Filename = HLE_IPC_BuildFilename(wii_path); + Offset += 64; + if (File::Delete(Filename)) + { + INFO_LOG(IOS_FILEIO, "FS: DeleteFile %s", Filename.c_str()); + } + else if (File::DeleteDir(Filename)) + { + INFO_LOG(IOS_FILEIO, "FS: DeleteDir %s", Filename.c_str()); + } + else + { + WARN_LOG(IOS_FILEIO, "FS: DeleteFile %s - failed!!!", Filename.c_str()); + } - // TODO: scrape the real amounts from somewhere... - fs.BlockSize = 0x4000; - fs.FreeUserBlocks = 0x5DEC; - fs.UsedUserBlocks = 0x1DD4; - fs.FreeSysBlocks = 0x10; - fs.UsedSysBlocks = 0x02F0; - fs.Free_INodes = 0x146B; - fs.Used_Inodes = 0x0394; + return GetFSReply(IPC_SUCCESS); +} - std::memcpy(Memory::GetPointer(request.buffer_out), &fs, sizeof(NANDStat)); +IPCCommandResult FS::RenameFile(const IOCtlRequest& request) +{ + _dbg_assert_(IOS_FILEIO, request.buffer_out_size == 0); + int Offset = 0; - return IPC_SUCCESS; + const std::string wii_path = Memory::GetString(request.buffer_in + Offset, 64); + if (!IsValidWiiPath(wii_path)) + { + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); + return GetFSReply(FS_EINVAL); } - break; + std::string Filename = HLE_IPC_BuildFilename(wii_path); + Offset += 64; - case IOCTL_CREATE_DIR: + const std::string wii_path_rename = Memory::GetString(request.buffer_in + Offset, 64); + if (!IsValidWiiPath(wii_path_rename)) { - _dbg_assert_(IOS_FILEIO, request.buffer_out_size == 0); - u32 Addr = request.buffer_in; - - u32 OwnerID = Memory::Read_U32(Addr); - Addr += 4; - u16 GroupID = Memory::Read_U16(Addr); - Addr += 2; - const std::string wii_path = Memory::GetString(Addr, 64); - if (!IsValidWiiPath(wii_path)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); - return FS_EINVAL; - } - std::string DirName(HLE_IPC_BuildFilename(wii_path)); - Addr += 64; - Addr += 9; // owner attribs, permission - u8 Attribs = Memory::Read_U8(Addr); + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path_rename.c_str()); + return GetFSReply(FS_EINVAL); + } - INFO_LOG(IOS_FILEIO, "FS: CREATE_DIR %s, OwnerID %#x, GroupID %#x, Attributes %#x", - DirName.c_str(), OwnerID, GroupID, Attribs); + std::string FilenameRename = HLE_IPC_BuildFilename(wii_path_rename); + Offset += 64; - DirName += DIR_SEP; - File::CreateFullPath(DirName); - _dbg_assert_msg_(IOS_FILEIO, File::IsDirectory(DirName), "FS: CREATE_DIR %s failed", - DirName.c_str()); + // try to make the basis directory + File::CreateFullPath(FilenameRename); - return IPC_SUCCESS; + // if there is already a file, delete it + if (File::Exists(Filename) && File::Exists(FilenameRename)) + { + File::Delete(FilenameRename); } - break; - case IOCTL_SET_ATTR: + // finally try to rename the file + if (File::Rename(Filename, FilenameRename)) + { + INFO_LOG(IOS_FILEIO, "FS: Rename %s to %s", Filename.c_str(), FilenameRename.c_str()); + } + else { - u32 Addr = request.buffer_in; + ERROR_LOG(IOS_FILEIO, "FS: Rename %s to %s - failed", Filename.c_str(), FilenameRename.c_str()); + return GetFSReply(FS_ENOENT); + } - u32 OwnerID = Memory::Read_U32(Addr); - Addr += 4; - u16 GroupID = Memory::Read_U16(Addr); - Addr += 2; - const std::string wii_path = Memory::GetString(Addr, 64); - if (!IsValidWiiPath(wii_path)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); - return FS_EINVAL; - } - std::string Filename = HLE_IPC_BuildFilename(wii_path); - Addr += 64; - u8 OwnerPerm = Memory::Read_U8(Addr); - Addr += 1; - u8 GroupPerm = Memory::Read_U8(Addr); - Addr += 1; - u8 OtherPerm = Memory::Read_U8(Addr); - Addr += 1; - u8 Attributes = Memory::Read_U8(Addr); - Addr += 1; + return GetFSReply(IPC_SUCCESS); +} + +IPCCommandResult FS::CreateFile(const IOCtlRequest& request) +{ + _dbg_assert_(IOS_FILEIO, request.buffer_out_size == 0); - INFO_LOG(IOS_FILEIO, "FS: SetAttrib %s", Filename.c_str()); - DEBUG_LOG(IOS_FILEIO, " OwnerID: 0x%08x", OwnerID); - DEBUG_LOG(IOS_FILEIO, " GroupID: 0x%04x", GroupID); - DEBUG_LOG(IOS_FILEIO, " OwnerPerm: 0x%02x", OwnerPerm); - DEBUG_LOG(IOS_FILEIO, " GroupPerm: 0x%02x", GroupPerm); - DEBUG_LOG(IOS_FILEIO, " OtherPerm: 0x%02x", OtherPerm); - DEBUG_LOG(IOS_FILEIO, " Attributes: 0x%02x", Attributes); + u32 Addr = request.buffer_in; + u32 OwnerID = Memory::Read_U32(Addr); + Addr += 4; + u16 GroupID = Memory::Read_U16(Addr); + Addr += 2; - return IPC_SUCCESS; + const std::string wii_path = Memory::GetString(Addr, 64); + if (!IsValidWiiPath(wii_path)) + { + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); + return GetFSReply(FS_EINVAL); } - break; - case IOCTL_GET_ATTR: + std::string Filename(HLE_IPC_BuildFilename(wii_path)); + Addr += 64; + u8 OwnerPerm = Memory::Read_U8(Addr); + Addr++; + u8 GroupPerm = Memory::Read_U8(Addr); + Addr++; + u8 OtherPerm = Memory::Read_U8(Addr); + Addr++; + u8 Attributes = Memory::Read_U8(Addr); + Addr++; + + INFO_LOG(IOS_FILEIO, "FS: CreateFile %s", Filename.c_str()); + DEBUG_LOG(IOS_FILEIO, " OwnerID: 0x%08x", OwnerID); + DEBUG_LOG(IOS_FILEIO, " GroupID: 0x%04x", GroupID); + DEBUG_LOG(IOS_FILEIO, " OwnerPerm: 0x%02x", OwnerPerm); + DEBUG_LOG(IOS_FILEIO, " GroupPerm: 0x%02x", GroupPerm); + DEBUG_LOG(IOS_FILEIO, " OtherPerm: 0x%02x", OtherPerm); + DEBUG_LOG(IOS_FILEIO, " Attributes: 0x%02x", Attributes); + + // check if the file already exist + if (File::Exists(Filename)) { - _dbg_assert_msg_(IOS_FILEIO, request.buffer_out_size == 76, - " GET_ATTR needs an 76 bytes large output buffer but it is %i bytes large", - request.buffer_out_size); + INFO_LOG(IOS_FILEIO, "\tresult = FS_EEXIST"); + return GetFSReply(FS_EEXIST); + } - u32 OwnerID = 0; - u16 GroupID = 0x3031; // this is also known as makercd, 01 (0x3031) for nintendo and 08 - // (0x3038) for MH3 etc - const std::string wii_path = Memory::GetString(request.buffer_in, 64); - if (!IsValidWiiPath(wii_path)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); - return FS_EINVAL; - } - std::string Filename = HLE_IPC_BuildFilename(wii_path); - u8 OwnerPerm = 0x3; // read/write - u8 GroupPerm = 0x3; // read/write - u8 OtherPerm = 0x3; // read/write - u8 Attributes = 0x00; // no attributes - if (File::IsDirectory(Filename)) - { - INFO_LOG(IOS_FILEIO, "FS: GET_ATTR Directory %s - all permission flags are set", - Filename.c_str()); - } - else - { - if (File::Exists(Filename)) - { - INFO_LOG(IOS_FILEIO, "FS: GET_ATTR %s - all permission flags are set", Filename.c_str()); - } - else - { - INFO_LOG(IOS_FILEIO, "FS: GET_ATTR unknown %s", Filename.c_str()); - return FS_ENOENT; - } - } + // create the file + File::CreateFullPath(Filename); // just to be sure + bool Result = File::CreateEmptyFile(Filename); + if (!Result) + { + ERROR_LOG(IOS_FILEIO, "FS: couldn't create new file"); + PanicAlert("FS: couldn't create new file"); + return GetFSReply(FS_EINVAL); + } - // write answer to buffer - if (request.buffer_out_size == 76) - { - u32 Addr = request.buffer_out; - Memory::Write_U32(OwnerID, Addr); - Addr += 4; - Memory::Write_U16(GroupID, Addr); - Addr += 2; - memcpy(Memory::GetPointer(Addr), Memory::GetPointer(request.buffer_in), 64); - Addr += 64; - Memory::Write_U8(OwnerPerm, Addr); - Addr += 1; - Memory::Write_U8(GroupPerm, Addr); - Addr += 1; - Memory::Write_U8(OtherPerm, Addr); - Addr += 1; - Memory::Write_U8(Attributes, Addr); - Addr += 1; - } + INFO_LOG(IOS_FILEIO, "\tresult = IPC_SUCCESS"); + return GetFSReply(IPC_SUCCESS); +} - return IPC_SUCCESS; - } - break; +IPCCommandResult FS::Shutdown(const IOCtlRequest& request) +{ + // TODO: stop emulation + INFO_LOG(IOS_FILEIO, "Wii called Shutdown()"); + return GetFSReply(IPC_SUCCESS); +} - case IOCTL_DELETE_FILE: +IPCCommandResult FS::ReadDirectory(const IOCtlVRequest& request) +{ + const std::string relative_path = + Memory::GetString(request.in_vectors[0].address, request.in_vectors[0].size); + + if (!IsValidWiiPath(relative_path)) { - _dbg_assert_(IOS_FILEIO, request.buffer_out_size == 0); - int Offset = 0; + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", relative_path.c_str()); + return GetFSReply(FS_EINVAL); + } - const std::string wii_path = Memory::GetString(request.buffer_in + Offset, 64); - if (!IsValidWiiPath(wii_path)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); - return FS_EINVAL; - } - std::string Filename = HLE_IPC_BuildFilename(wii_path); - Offset += 64; - if (File::Delete(Filename)) - { - INFO_LOG(IOS_FILEIO, "FS: DeleteFile %s", Filename.c_str()); - } - else if (File::DeleteDir(Filename)) - { - INFO_LOG(IOS_FILEIO, "FS: DeleteDir %s", Filename.c_str()); - } - else - { - WARN_LOG(IOS_FILEIO, "FS: DeleteFile %s - failed!!!", Filename.c_str()); - } + // the Wii uses this function to define the type (dir or file) + std::string DirName(HLE_IPC_BuildFilename(relative_path)); - return IPC_SUCCESS; - } - break; + INFO_LOG(IOS_FILEIO, "FS: IOCTL_READ_DIR %s", DirName.c_str()); - case IOCTL_RENAME_FILE: + if (!File::Exists(DirName)) { - _dbg_assert_(IOS_FILEIO, request.buffer_out_size == 0); - int Offset = 0; + WARN_LOG(IOS_FILEIO, "FS: Search not found: %s", DirName.c_str()); + return GetFSReply(FS_ENOENT); + } - const std::string wii_path = Memory::GetString(request.buffer_in + Offset, 64); - if (!IsValidWiiPath(wii_path)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); - return FS_EINVAL; - } - std::string Filename = HLE_IPC_BuildFilename(wii_path); - Offset += 64; + if (!File::IsDirectory(DirName)) + { + // It's not a directory, so error. + // Games don't usually seem to care WHICH error they get, as long as it's < + // Well the system menu CARES! + WARN_LOG(IOS_FILEIO, "\tNot a directory - return FS_EINVAL"); + return GetFSReply(FS_EINVAL); + } - const std::string wii_path_rename = Memory::GetString(request.buffer_in + Offset, 64); - if (!IsValidWiiPath(wii_path_rename)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path_rename.c_str()); - return FS_EINVAL; - } - std::string FilenameRename = HLE_IPC_BuildFilename(wii_path_rename); - Offset += 64; + File::FSTEntry entry = File::ScanDirectoryTree(DirName, false); - // try to make the basis directory - File::CreateFullPath(FilenameRename); + // it is one + if ((request.in_vectors.size() == 1) && (request.io_vectors.size() == 1)) + { + size_t numFile = entry.children.size(); + INFO_LOG(IOS_FILEIO, "\t%zu files found", numFile); - // if there is already a file, delete it - if (File::Exists(Filename) && File::Exists(FilenameRename)) + Memory::Write_U32((u32)numFile, request.io_vectors[0].address); + } + else + { + for (File::FSTEntry& child : entry.children) { - File::Delete(FilenameRename); + // Decode escaped invalid file system characters so that games (such as + // Harry Potter and the Half-Blood Prince) can find what they expect. + child.virtualName = Common::UnescapeFileName(child.virtualName); } - // finally try to rename the file - if (File::Rename(Filename, FilenameRename)) - { - INFO_LOG(IOS_FILEIO, "FS: Rename %s to %s", Filename.c_str(), FilenameRename.c_str()); - } - else - { - ERROR_LOG(IOS_FILEIO, "FS: Rename %s to %s - failed", Filename.c_str(), - FilenameRename.c_str()); - return FS_ENOENT; - } + std::sort(entry.children.begin(), entry.children.end(), + [](const File::FSTEntry& one, const File::FSTEntry& two) { + return one.virtualName < two.virtualName; + }); - return IPC_SUCCESS; - } - break; + u32 MaxEntries = Memory::Read_U32(request.in_vectors[0].address); - case IOCTL_CREATE_FILE: - { - _dbg_assert_(IOS_FILEIO, request.buffer_out_size == 0); + memset(Memory::GetPointer(request.io_vectors[0].address), 0, request.io_vectors[0].size); - u32 Addr = request.buffer_in; - u32 OwnerID = Memory::Read_U32(Addr); - Addr += 4; - u16 GroupID = Memory::Read_U16(Addr); - Addr += 2; - const std::string wii_path = Memory::GetString(Addr, 64); - if (!IsValidWiiPath(wii_path)) - { - WARN_LOG(IOS_FILEIO, "Not a valid path: %s", wii_path.c_str()); - return FS_EINVAL; - } - std::string Filename(HLE_IPC_BuildFilename(wii_path)); - Addr += 64; - u8 OwnerPerm = Memory::Read_U8(Addr); - Addr++; - u8 GroupPerm = Memory::Read_U8(Addr); - Addr++; - u8 OtherPerm = Memory::Read_U8(Addr); - Addr++; - u8 Attributes = Memory::Read_U8(Addr); - Addr++; - - INFO_LOG(IOS_FILEIO, "FS: CreateFile %s", Filename.c_str()); - DEBUG_LOG(IOS_FILEIO, " OwnerID: 0x%08x", OwnerID); - DEBUG_LOG(IOS_FILEIO, " GroupID: 0x%04x", GroupID); - DEBUG_LOG(IOS_FILEIO, " OwnerPerm: 0x%02x", OwnerPerm); - DEBUG_LOG(IOS_FILEIO, " GroupPerm: 0x%02x", GroupPerm); - DEBUG_LOG(IOS_FILEIO, " OtherPerm: 0x%02x", OtherPerm); - DEBUG_LOG(IOS_FILEIO, " Attributes: 0x%02x", Attributes); - - // check if the file already exist - if (File::Exists(Filename)) - { - INFO_LOG(IOS_FILEIO, "\tresult = FS_EEXIST"); - return FS_EEXIST; - } + size_t numFiles = 0; + char* pFilename = (char*)Memory::GetPointer((u32)(request.io_vectors[0].address)); - // create the file - File::CreateFullPath(Filename); // just to be sure - bool Result = File::CreateEmptyFile(Filename); - if (!Result) + for (size_t i = 0; i < entry.children.size() && i < MaxEntries; i++) { - ERROR_LOG(IOS_FILEIO, "FS: couldn't create new file"); - PanicAlert("FS: couldn't create new file"); - return FS_EINVAL; + const std::string& FileName = entry.children[i].virtualName; + + strcpy(pFilename, FileName.c_str()); + pFilename += FileName.length(); + *pFilename++ = 0x00; // termination + numFiles++; + + INFO_LOG(IOS_FILEIO, "\tFound: %s", FileName.c_str()); } - INFO_LOG(IOS_FILEIO, "\tresult = IPC_SUCCESS"); - return IPC_SUCCESS; - } - break; - case IOCTL_SHUTDOWN: - { - INFO_LOG(IOS_FILEIO, "Wii called Shutdown()"); - // TODO: stop emulation - } - break; - default: - request.DumpUnknown(GetDeviceName(), LogTypes::IOS_FILEIO); + Memory::Write_U32((u32)numFiles, request.io_vectors[1].address); } - return FS_EINVAL; + return GetFSReply(IPC_SUCCESS); } -void FS::DoState(PointerWrap& p) +IPCCommandResult FS::GetUsage(const IOCtlVRequest& request) { - DoStateShared(p); + _dbg_assert_(IOS_FILEIO, request.io_vectors.size() == 2); + _dbg_assert_(IOS_FILEIO, request.io_vectors[0].size == 4); + _dbg_assert_(IOS_FILEIO, request.io_vectors[1].size == 4); - // handle /tmp + // this command sucks because it asks of the number of used + // fsBlocks and inodes + // It should be correct, but don't count on it... + std::string relativepath = + Memory::GetString(request.in_vectors[0].address, request.in_vectors[0].size); - std::string Path = File::GetUserPath(D_SESSION_WIIROOT_IDX) + "/tmp"; - if (p.GetMode() == PointerWrap::MODE_READ) + if (!IsValidWiiPath(relativepath)) { - File::DeleteDirRecursively(Path); - File::CreateDir(Path); + WARN_LOG(IOS_FILEIO, "Not a valid path: %s", relativepath.c_str()); + return GetFSReply(FS_EINVAL); + } - // now restore from the stream - while (1) + std::string path(HLE_IPC_BuildFilename(relativepath)); + u32 fsBlocks = 0; + u32 iNodes = 0; + + INFO_LOG(IOS_FILEIO, "IOCTL_GETUSAGE %s", path.c_str()); + if (File::IsDirectory(path)) + { + // LPFaint99: After I found that setting the number of inodes to the number of children + 1 + // for the directory itself + // I decided to compare with sneek which has the following 2 special cases which are + // Copyright (C) 2009-2011 crediar http://code.google.com/p/sneek/ + if ((relativepath.compare(0, 16, "/title/00010001") == 0) || + (relativepath.compare(0, 16, "/title/00010005") == 0)) { - char type = 0; - p.Do(type); - if (!type) - break; - std::string filename; - p.Do(filename); - std::string name = Path + DIR_SEP + filename; - switch (type) - { - case 'd': - { - File::CreateDir(name); - break; - } - case 'f': - { - u32 size = 0; - p.Do(size); + fsBlocks = 23; // size is size/0x4000 + iNodes = 42; // empty folders return a FileCount of 1 + } + else + { + File::FSTEntry parentDir = File::ScanDirectoryTree(path, true); + // add one for the folder itself + iNodes = 1 + (u32)parentDir.size; - File::IOFile handle(name, "wb"); - char buf[65536]; - u32 count = size; - while (count > 65536) - { - p.DoArray(buf); - handle.WriteArray(&buf[0], 65536); - count -= 65536; - } - p.DoArray(&buf[0], count); - handle.WriteArray(&buf[0], count); - break; - } - } + u64 totalSize = + ComputeTotalFileSize(parentDir); // "Real" size, to be converted to nand blocks + + fsBlocks = (u32)(totalSize / (16 * 1024)); // one bock is 16kb } + + INFO_LOG(IOS_FILEIO, "FS: fsBlock: %i, iNodes: %i", fsBlocks, iNodes); } else { - // recurse through tmp and save dirs and files - - File::FSTEntry parentEntry = File::ScanDirectoryTree(Path, true); - std::deque<File::FSTEntry> todo; - todo.insert(todo.end(), parentEntry.children.begin(), parentEntry.children.end()); - - while (!todo.empty()) - { - File::FSTEntry& entry = todo.front(); - std::string name = entry.physicalName; - name.erase(0, Path.length() + 1); - char type = entry.isDirectory ? 'd' : 'f'; - p.Do(type); - p.Do(name); - if (entry.isDirectory) - { - todo.insert(todo.end(), entry.children.begin(), entry.children.end()); - } - else - { - u32 size = (u32)entry.size; - p.Do(size); + fsBlocks = 0; + iNodes = 0; + WARN_LOG(IOS_FILEIO, "FS: fsBlock failed, cannot find directory: %s", path.c_str()); + } - File::IOFile handle(entry.physicalName, "rb"); - char buf[65536]; - u32 count = size; - while (count > 65536) - { - handle.ReadArray(&buf[0], 65536); - p.DoArray(buf); - count -= 65536; - } - handle.ReadArray(&buf[0], count); - p.DoArray(&buf[0], count); - } - todo.pop_front(); - } + Memory::Write_U32(fsBlocks, request.io_vectors[0].address); + Memory::Write_U32(iNodes, request.io_vectors[1].address); - char type = 0; - p.Do(type); - } + return GetFSReply(IPC_SUCCESS); } } // namespace Device } // namespace HLE diff --git a/Source/Core/Core/IOS/FS/FS.h b/Source/Core/Core/IOS/FS/FS.h index b40038b391..cec7983df7 100644 --- a/Source/Core/Core/IOS/FS/FS.h +++ b/Source/Core/Core/IOS/FS/FS.h @@ -56,7 +56,18 @@ private: }; IPCCommandResult GetFSReply(s32 return_value) const; - s32 ExecuteCommand(const IOCtlRequest& request); + + IPCCommandResult GetStats(const IOCtlRequest& request); + IPCCommandResult CreateDirectory(const IOCtlRequest& request); + IPCCommandResult SetAttribute(const IOCtlRequest& request); + IPCCommandResult GetAttribute(const IOCtlRequest& request); + IPCCommandResult DeleteFile(const IOCtlRequest& request); + IPCCommandResult RenameFile(const IOCtlRequest& request); + IPCCommandResult CreateFile(const IOCtlRequest& request); + IPCCommandResult Shutdown(const IOCtlRequest& request); + + IPCCommandResult ReadDirectory(const IOCtlVRequest& request); + IPCCommandResult GetUsage(const IOCtlVRequest& request); }; } // namespace Device } // namespace HLE |
