Play-/Source/iop/Iop_Ioman.cpp
2019-10-23 20:48:46 -04:00

840 lines
24 KiB
C++

#include <cstring>
#include <stdexcept>
#include <cctype>
#include "StdStream.h"
#include "xml/Utils.h"
#include "std_experimental_map.h"
#include "Iop_Ioman.h"
#include "IopBios.h"
#include "../AppConfig.h"
#include "../Log.h"
#include "../states/XmlStateFile.h"
using namespace Iop;
#define LOG_NAME "iop_ioman"
#define STATE_FILES_FILENAME ("iop_ioman/files.xml")
#define STATE_FILES_FILESNODE "Files"
#define STATE_FILES_FILENODE "File"
#define STATE_FILES_FILENODE_IDATTRIBUTE ("Id")
#define STATE_FILES_FILENODE_PATHATTRIBUTE ("Path")
#define STATE_FILES_FILENODE_FLAGSATTRIBUTE ("Flags")
#define STATE_FILES_FILENODE_DESCPTRATTRIBUTE ("DescPtr")
#define STATE_USERDEVICES_FILENAME ("iop_ioman/userdevices.xml")
#define STATE_USERDEVICES_DEVICESNODE "Devices"
#define STATE_USERDEVICES_DEVICENODE "Device"
#define STATE_USERDEVICES_DEVICENODE_NAMEATTRIBUTE ("Name")
#define STATE_USERDEVICES_DEVICENODE_DESCPTRATTRIBUTE ("DescPtr")
#define PREF_IOP_FILEIO_STDLOGGING ("iop.fileio.stdlogging")
#define FUNCTION_ADDDRV "AddDrv"
#define FUNCTION_DELDRV "DelDrv"
//Directories have "group read" only permissions? This is required by PS2PSXe.
#define STAT_MODE_DIR (0747 | (1 << 12)) //File mode + Dir type (1)
#define STAT_MODE_FILE (0777 | (2 << 12)) //File mode + File type (2)
static std::string RightTrim(std::string inputString)
{
auto nonSpaceEnd = std::find_if(inputString.rbegin(), inputString.rend(), [](int ch) { return !std::isspace(ch); });
inputString.erase(nonSpaceEnd.base(), inputString.end());
return inputString;
}
struct PATHINFO
{
std::string deviceName;
std::string devicePath;
};
static PATHINFO SplitPath(const char* path)
{
std::string fullPath(path);
auto position = fullPath.find(":");
if(position == std::string::npos)
{
throw std::runtime_error("Invalid path.");
}
PATHINFO result;
result.deviceName = std::string(fullPath.begin(), fullPath.begin() + position);
result.devicePath = std::string(fullPath.begin() + position + 1, fullPath.end());
//Some games (Street Fighter EX3) provide paths with trailing spaces
result.devicePath = RightTrim(result.devicePath);
return result;
}
CIoman::CIoman(CIopBios& bios, uint8* ram)
: m_bios(bios)
, m_ram(ram)
, m_nextFileHandle(3)
{
CAppConfig::GetInstance().RegisterPreferenceBoolean(PREF_IOP_FILEIO_STDLOGGING, false);
//Insert standard files if requested.
if(CAppConfig::GetInstance().GetPreferenceBoolean(PREF_IOP_FILEIO_STDLOGGING)
#ifdef DEBUGGER_INCLUDED
|| true
#endif
)
{
try
{
auto stdoutPath = CAppConfig::GetBasePath() / "ps2_stdout.txt";
auto stderrPath = CAppConfig::GetBasePath() / "ps2_stderr.txt";
m_files[FID_STDOUT] = FileInfo{new Framework::CStdStream(fopen(stdoutPath.string().c_str(), "ab"))};
m_files[FID_STDERR] = FileInfo{new Framework::CStdStream(fopen(stderrPath.string().c_str(), "ab"))};
}
catch(...)
{
//Humm, some error occured when opening these files...
}
}
}
CIoman::~CIoman()
{
m_files.clear();
m_devices.clear();
}
void CIoman::PrepareOpenThunk()
{
if(m_openThunkPtr != 0) return;
static const uint32 thunkSize = 0x30;
auto sysmem = m_bios.GetSysmem();
m_openThunkPtr = sysmem->AllocateMemory(thunkSize, 0, 0);
static const int16 stackAlloc = 0x10;
CMIPSAssembler assembler(reinterpret_cast<uint32*>(m_ram + m_openThunkPtr));
auto finishLabel = assembler.CreateLabel();
//Save return value (stored in T0)
assembler.ADDIU(CMIPS::SP, CMIPS::SP, -stackAlloc);
assembler.SW(CMIPS::RA, 0x00, CMIPS::SP);
//Call open handler
assembler.JALR(CMIPS::A3);
assembler.SW(CMIPS::T0, 0x04, CMIPS::SP);
//Check if open handler reported error, if so, leave return value untouched
//TODO: Release handle if we failed to open properly
assembler.BLTZ(CMIPS::V0, finishLabel);
assembler.LW(CMIPS::RA, 0x00, CMIPS::SP);
assembler.LW(CMIPS::V0, 0x04, CMIPS::SP);
assembler.MarkLabel(finishLabel);
assembler.JR(CMIPS::RA);
assembler.ADDIU(CMIPS::SP, CMIPS::SP, stackAlloc);
assert((assembler.GetProgramSize() * 4) <= thunkSize);
}
std::string CIoman::GetId() const
{
return "ioman";
}
std::string CIoman::GetFunctionName(unsigned int functionId) const
{
switch(functionId)
{
case 4:
return "open";
break;
case 5:
return "close";
break;
case 6:
return "read";
break;
case 8:
return "seek";
break;
case 16:
return "getstat";
break;
case 20:
return FUNCTION_ADDDRV;
break;
case 21:
return FUNCTION_DELDRV;
break;
default:
return "unknown";
break;
}
}
void CIoman::RegisterDevice(const char* name, const DevicePtr& device)
{
m_devices[name] = device;
}
uint32 CIoman::Open(uint32 flags, const char* path)
{
CLog::GetInstance().Print(LOG_NAME, "Open(flags = 0x%08X, path = '%s');\r\n", flags, path);
int32 handle = PreOpen(flags, path);
if(handle < 0)
{
return handle;
}
assert(!IsUserDeviceFileHandle(handle));
return handle;
}
Framework::CStream* CIoman::OpenInternal(uint32 flags, const char* path)
{
auto pathInfo = SplitPath(path);
auto deviceIterator = m_devices.find(pathInfo.deviceName);
if(deviceIterator == m_devices.end())
{
throw std::runtime_error("Device not found.");
}
auto stream = deviceIterator->second->GetFile(flags, pathInfo.devicePath.c_str());
if(!stream)
{
throw std::runtime_error("File not found.");
}
return stream;
}
uint32 CIoman::Close(uint32 handle)
{
CLog::GetInstance().Print(LOG_NAME, "Close(handle = %d);\r\n", handle);
uint32 result = 0xFFFFFFFF;
assert(!IsUserDeviceFileHandle(handle));
try
{
auto file(m_files.find(handle));
if(file == std::end(m_files))
{
throw std::runtime_error("Invalid file handle.");
}
FreeFileHandle(handle);
//Returns handle instead of 0 (needed by Naruto: Ultimate Ninja 2)
result = handle;
}
catch(const std::exception& except)
{
CLog::GetInstance().Warn(LOG_NAME, "%s: Error occured while trying to close file : %s\r\n", __FUNCTION__, except.what());
}
return result;
}
uint32 CIoman::Read(uint32 handle, uint32 size, void* buffer)
{
CLog::GetInstance().Print(LOG_NAME, "Read(handle = %d, size = 0x%X, buffer = ptr);\r\n", handle, size);
uint32 result = 0xFFFFFFFF;
assert(!IsUserDeviceFileHandle(handle));
try
{
auto stream = GetFileStream(handle);
if(stream->IsEOF())
{
result = 0;
}
else
{
result = static_cast<uint32>(stream->Read(buffer, size));
}
}
catch(const std::exception& except)
{
CLog::GetInstance().Warn(LOG_NAME, "%s: Error occured while trying to read file : %s\r\n", __FUNCTION__, except.what());
}
return result;
}
uint32 CIoman::Write(uint32 handle, uint32 size, const void* buffer)
{
CLog::GetInstance().Print(LOG_NAME, "Write(handle = %d, size = 0x%X, buffer = ptr);\r\n", handle, size);
uint32 result = 0xFFFFFFFF;
assert(!IsUserDeviceFileHandle(handle));
try
{
auto stream = GetFileStream(handle);
result = static_cast<uint32>(stream->Write(buffer, size));
if((handle == FID_STDOUT) || (handle == FID_STDERR))
{
//Force flusing stdout and stderr
stream->Flush();
}
}
catch(const std::exception& except)
{
if((handle != FID_STDOUT) && (handle != FID_STDERR))
{
CLog::GetInstance().Warn(LOG_NAME, "%s: Error occured while trying to write file : %s\r\n", __FUNCTION__, except.what());
}
}
return result;
}
uint32 CIoman::Seek(uint32 handle, uint32 position, uint32 whence)
{
CLog::GetInstance().Print(LOG_NAME, "Seek(handle = %d, position = 0x%X, whence = %d);\r\n",
handle, position, whence);
uint32 result = 0xFFFFFFFF;
assert(!IsUserDeviceFileHandle(handle));
try
{
auto stream = GetFileStream(handle);
switch(whence)
{
case SEEK_DIR_SET:
whence = Framework::STREAM_SEEK_SET;
break;
case SEEK_DIR_CUR:
whence = Framework::STREAM_SEEK_CUR;
break;
case SEEK_DIR_END:
whence = Framework::STREAM_SEEK_END;
break;
}
stream->Seek(position, static_cast<Framework::STREAM_SEEK_DIRECTION>(whence));
result = static_cast<uint32>(stream->Tell());
}
catch(const std::exception& except)
{
CLog::GetInstance().Warn(LOG_NAME, "%s: Error occured while trying to seek file : %s\r\n", __FUNCTION__, except.what());
}
return result;
}
int32 CIoman::Dopen(const char* path)
{
CLog::GetInstance().Print(LOG_NAME, "Dopen(path = '%s');\r\n",
path);
int32 handle = -1;
try
{
auto pathInfo = SplitPath(path);
auto deviceIterator = m_devices.find(pathInfo.deviceName);
if(deviceIterator == m_devices.end())
{
throw std::runtime_error("Device not found.");
}
auto directory = deviceIterator->second->GetDirectory(pathInfo.devicePath.c_str());
handle = m_nextFileHandle++;
m_directories[handle] = directory;
}
catch(const std::exception& except)
{
CLog::GetInstance().Warn(LOG_NAME, "%s: Error occured while trying to open directory : %s\r\n", __FUNCTION__, except.what());
}
return handle;
}
int32 CIoman::Dclose(uint32 handle)
{
CLog::GetInstance().Print(LOG_NAME, "Dclose(handle = %d);\r\n",
handle);
auto directoryIterator = m_directories.find(handle);
if(directoryIterator == std::end(m_directories))
{
return -1;
}
m_directories.erase(directoryIterator);
return 0;
}
int32 CIoman::Dread(uint32 handle, Ioman::DIRENTRY* dirEntry)
{
CLog::GetInstance().Print(LOG_NAME, "Dread(handle = %d, entry = ptr);\r\n",
handle);
auto directoryIterator = m_directories.find(handle);
if(directoryIterator == std::end(m_directories))
{
return -1;
}
auto& directory = directoryIterator->second;
if(directory == Ioman::Directory())
{
return 0;
}
auto itemPath = directory->path();
auto name = itemPath.filename().string();
strncpy(dirEntry->name, name.c_str(), Ioman::DIRENTRY::NAME_SIZE);
dirEntry->name[Ioman::DIRENTRY::NAME_SIZE - 1] = 0;
auto& stat = dirEntry->stat;
memset(&stat, 0, sizeof(Ioman::STAT));
if(fs::is_directory(itemPath))
{
stat.mode = STAT_MODE_DIR;
stat.attr = 0x8427;
}
else
{
stat.mode = STAT_MODE_FILE;
stat.loSize = fs::file_size(itemPath);
stat.attr = 0x8497;
}
directory++;
return strlen(dirEntry->name);
}
uint32 CIoman::GetStat(const char* path, Ioman::STAT* stat)
{
CLog::GetInstance().Print(LOG_NAME, "GetStat(path = '%s', stat = ptr);\r\n", path);
//Try with a file
{
int32 fd = Open(Ioman::CDevice::OPEN_FLAG_RDONLY, path);
if(fd >= 0)
{
uint32 size = Seek(fd, 0, SEEK_DIR_END);
Close(fd);
memset(stat, 0, sizeof(Ioman::STAT));
stat->mode = STAT_MODE_FILE;
stat->loSize = size;
return 0;
}
}
//Try with a directory
{
int32 fd = Dopen(path);
if(fd >= 0)
{
Dclose(fd);
memset(stat, 0, sizeof(Ioman::STAT));
stat->mode = STAT_MODE_DIR;
return 0;
}
}
return -1;
}
int32 CIoman::AddDrv(CMIPS& context)
{
auto devicePtr = context.m_State.nGPR[CMIPS::A0].nV0;
CLog::GetInstance().Print(LOG_NAME, FUNCTION_ADDDRV "(devicePtr = 0x%08X);\r\n",
devicePtr);
auto device = reinterpret_cast<const Ioman::DEVICE*>(m_ram + devicePtr);
auto deviceName = device->namePtr ? reinterpret_cast<const char*>(m_ram + device->namePtr) : nullptr;
auto deviceDesc = device->descPtr ? reinterpret_cast<const char*>(m_ram + device->descPtr) : nullptr;
CLog::GetInstance().Print(LOG_NAME, "Requested registration of device '%s'.\r\n", deviceName);
//We only support "cdfs" for now
if(!deviceName || strcmp(deviceName, "cdfs"))
{
return -1;
}
m_userDevices.insert(std::make_pair(deviceName, devicePtr));
InvokeUserDeviceMethod(context, devicePtr, offsetof(Ioman::DEVICEOPS, initPtr), devicePtr);
return 0;
}
uint32 CIoman::DelDrv(uint32 drvNamePtr)
{
CLog::GetInstance().Print(LOG_NAME, FUNCTION_DELDRV "(drvNamePtr = %s);\r\n",
PrintStringParameter(m_ram, drvNamePtr).c_str());
return -1;
}
int32 CIoman::PreOpen(uint32 flags, const char* path)
{
if(flags == 0)
{
//If no flags provided, assume we want to open the file
//Required by Capcom Arcade Collection
flags = Ioman::CDevice::OPEN_FLAG_RDONLY;
}
int32 handle = AllocateFileHandle();
try
{
auto& file = m_files[handle];
file.path = path;
file.flags = flags;
auto pathInfo = SplitPath(path);
auto deviceIterator = m_devices.find(pathInfo.deviceName);
auto userDeviceIterator = m_userDevices.find(pathInfo.deviceName);
if(deviceIterator != m_devices.end())
{
file.stream = deviceIterator->second->GetFile(flags, pathInfo.devicePath.c_str());
if(!file.stream)
{
throw std::runtime_error("File not found.");
}
}
else if(userDeviceIterator != m_userDevices.end())
{
auto sysmem = m_bios.GetSysmem();
file.descPtr = sysmem->AllocateMemory(sizeof(Ioman::DEVICEFILE), 0, 0);
assert(file.descPtr != 0);
auto desc = reinterpret_cast<Ioman::DEVICEFILE*>(m_ram + file.descPtr);
desc->devicePtr = userDeviceIterator->second;
desc->privateData = 0;
desc->unit = 0;
desc->mode = flags;
}
else
{
throw std::runtime_error("Unknown device.");
}
}
catch(const std::exception& except)
{
CLog::GetInstance().Warn(LOG_NAME, "%s: Error occured while trying to open file : %s\r\n", __FUNCTION__, except.what());
FreeFileHandle(handle);
return -1;
}
return handle;
}
bool CIoman::IsUserDeviceFileHandle(int32 fileHandle) const
{
auto fileIterator = m_files.find(fileHandle);
if(fileIterator == std::end(m_files)) return false;
return GetUserDeviceFileDescPtr(fileHandle) != 0;
}
uint32 CIoman::GetUserDeviceFileDescPtr(int32 fileHandle) const
{
auto fileIterator = m_files.find(fileHandle);
assert(fileIterator != std::end(m_files));
const auto& file = fileIterator->second;
assert(!((file.descPtr != 0) && file.stream));
return file.descPtr;
}
int32 CIoman::OpenVirtual(CMIPS& context)
{
uint32 pathPtr = context.m_State.nGPR[CMIPS::A0].nV0;
uint32 flags = context.m_State.nGPR[CMIPS::A1].nV0;
auto path = reinterpret_cast<const char*>(m_ram + pathPtr);
int32 handle = PreOpen(flags, path);
if(handle < 0)
{
//PreOpen failed, bail
return handle;
}
if(IsUserDeviceFileHandle(handle))
{
PrepareOpenThunk();
auto devicePath = strchr(path, ':');
assert(devicePath);
auto devicePathPos = static_cast<uint32>(devicePath - path);
uint32 descPtr = GetUserDeviceFileDescPtr(handle);
auto desc = reinterpret_cast<Ioman::DEVICEFILE*>(m_ram + descPtr);
auto device = reinterpret_cast<Ioman::DEVICE*>(m_ram + desc->devicePtr);
auto ops = reinterpret_cast<Ioman::DEVICEOPS*>(m_ram + device->opsPtr);
context.m_State.nPC = m_openThunkPtr;
context.m_State.nGPR[CMIPS::A0].nV0 = descPtr;
context.m_State.nGPR[CMIPS::A1].nV0 = pathPtr + devicePathPos + 1;
context.m_State.nGPR[CMIPS::A2].nV0 = flags;
context.m_State.nGPR[CMIPS::A3].nV0 = ops->openPtr;
context.m_State.nGPR[CMIPS::T0].nV0 = handle;
return 0;
}
return handle;
}
int32 CIoman::CloseVirtual(CMIPS& context)
{
int32 handle = context.m_State.nGPR[CMIPS::A0].nV0;
auto fileIterator = m_files.find(handle);
if(fileIterator == std::end(m_files))
{
CLog::GetInstance().Warn(LOG_NAME, "%s : Provided invalid fd %d.\r\n",
__FUNCTION__, handle);
return -1;
}
if(IsUserDeviceFileHandle(handle))
{
//TODO: Free file handle
uint32 descPtr = GetUserDeviceFileDescPtr(handle);
auto desc = reinterpret_cast<Ioman::DEVICEFILE*>(m_ram + descPtr);
InvokeUserDeviceMethod(context, desc->devicePtr,
offsetof(Ioman::DEVICEOPS, closePtr),
descPtr);
return 0;
}
else
{
return Close(handle);
}
}
int32 CIoman::ReadVirtual(CMIPS& context)
{
int32 handle = context.m_State.nGPR[CMIPS::A0].nV0;
uint32 bufferPtr = context.m_State.nGPR[CMIPS::A1].nV0;
uint32 count = context.m_State.nGPR[CMIPS::A2].nV0;
auto fileIterator = m_files.find(handle);
if(fileIterator == std::end(m_files))
{
CLog::GetInstance().Warn(LOG_NAME, "%s : Provided invalid fd %d.\r\n",
__FUNCTION__, handle);
return -1;
}
if(IsUserDeviceFileHandle(handle))
{
uint32 descPtr = GetUserDeviceFileDescPtr(handle);
auto desc = reinterpret_cast<Ioman::DEVICEFILE*>(m_ram + descPtr);
InvokeUserDeviceMethod(context, desc->devicePtr,
offsetof(Ioman::DEVICEOPS, readPtr),
descPtr, bufferPtr, count);
return 0;
}
else
{
return Read(handle, count, m_ram + bufferPtr);
}
}
int32 CIoman::SeekVirtual(CMIPS& context)
{
int32 handle = context.m_State.nGPR[CMIPS::A0].nV0;
uint32 position = context.m_State.nGPR[CMIPS::A1].nV0;
uint32 whence = context.m_State.nGPR[CMIPS::A2].nV0;
auto fileIterator = m_files.find(handle);
if(fileIterator == std::end(m_files))
{
CLog::GetInstance().Warn(LOG_NAME, "%s : Provided invalid fd %d.\r\n",
__FUNCTION__, handle);
return -1;
}
if(IsUserDeviceFileHandle(handle))
{
uint32 descPtr = GetUserDeviceFileDescPtr(handle);
auto desc = reinterpret_cast<Ioman::DEVICEFILE*>(m_ram + descPtr);
InvokeUserDeviceMethod(context, desc->devicePtr,
offsetof(Ioman::DEVICEOPS, lseekPtr),
descPtr, position, whence);
return 0;
}
else
{
return Seek(handle, position, whence);
}
}
void CIoman::InvokeUserDeviceMethod(CMIPS& context, uint32 devicePtr, size_t opOffset, uint32 arg0, uint32 arg1, uint32 arg2)
{
auto device = reinterpret_cast<Ioman::DEVICE*>(m_ram + devicePtr);
auto opAddr = *reinterpret_cast<uint32*>(m_ram + device->opsPtr + opOffset);
context.m_State.nGPR[CMIPS::A0].nV0 = arg0;
context.m_State.nGPR[CMIPS::A1].nV0 = arg1;
context.m_State.nGPR[CMIPS::A2].nV0 = arg2;
context.m_State.nPC = opAddr;
}
int32 CIoman::AllocateFileHandle()
{
uint32 handle = m_nextFileHandle++;
assert(m_files.find(handle) == std::end(m_files));
m_files[handle] = FileInfo();
return handle;
}
void CIoman::FreeFileHandle(uint32 handle)
{
assert(m_files.find(handle) != std::end(m_files));
m_files.erase(handle);
}
uint32 CIoman::GetFileMode(uint32 handle) const
{
auto file(m_files.find(handle));
if(file == std::end(m_files))
{
throw std::runtime_error("Invalid file handle.");
}
return file->second.flags;
}
Framework::CStream* CIoman::GetFileStream(uint32 handle)
{
auto file(m_files.find(handle));
if(file == std::end(m_files))
{
throw std::runtime_error("Invalid file handle.");
}
return file->second.stream;
}
void CIoman::SetFileStream(uint32 handle, Framework::CStream* stream)
{
m_files.erase(handle);
m_files[handle] = {stream};
}
//IOP Invoke
void CIoman::Invoke(CMIPS& context, unsigned int functionId)
{
switch(functionId)
{
case 4:
context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(OpenVirtual(context));
break;
case 5:
context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(CloseVirtual(context));
break;
case 6:
context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(ReadVirtual(context));
break;
case 8:
context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(SeekVirtual(context));
break;
case 16:
context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(GetStat(
reinterpret_cast<char*>(&m_ram[context.m_State.nGPR[CMIPS::A0].nV[0]]),
reinterpret_cast<Ioman::STAT*>(&m_ram[context.m_State.nGPR[CMIPS::A1].nV[0]])));
break;
case 20:
context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(AddDrv(context));
break;
case 21:
context.m_State.nGPR[CMIPS::V0].nD0 = static_cast<int32>(DelDrv(
context.m_State.nGPR[CMIPS::A0].nV0));
break;
default:
CLog::GetInstance().Warn(LOG_NAME, "%s(%08X): Unknown function (%d) called.\r\n", __FUNCTION__, context.m_State.nPC, functionId);
break;
}
}
void CIoman::SaveState(Framework::CZipArchiveWriter& archive)
{
SaveFilesState(archive);
SaveUserDevicesState(archive);
}
void CIoman::LoadState(Framework::CZipArchiveReader& archive)
{
LoadFilesState(archive);
LoadUserDevicesState(archive);
}
void CIoman::SaveFilesState(Framework::CZipArchiveWriter& archive)
{
auto fileStateFile = new CXmlStateFile(STATE_FILES_FILENAME, STATE_FILES_FILESNODE);
auto filesStateNode = fileStateFile->GetRoot();
for(const auto& filePair : m_files)
{
if(filePair.first == FID_STDOUT) continue;
if(filePair.first == FID_STDERR) continue;
const auto& file = filePair.second;
auto fileStateNode = new Framework::Xml::CNode(STATE_FILES_FILENODE, true);
fileStateNode->InsertAttribute(Framework::Xml::CreateAttributeIntValue(STATE_FILES_FILENODE_IDATTRIBUTE, filePair.first));
fileStateNode->InsertAttribute(Framework::Xml::CreateAttributeIntValue(STATE_FILES_FILENODE_FLAGSATTRIBUTE, file.flags));
fileStateNode->InsertAttribute(Framework::Xml::CreateAttributeIntValue(STATE_FILES_FILENODE_DESCPTRATTRIBUTE, file.descPtr));
fileStateNode->InsertAttribute(Framework::Xml::CreateAttributeStringValue(STATE_FILES_FILENODE_PATHATTRIBUTE, file.path.c_str()));
filesStateNode->InsertNode(fileStateNode);
}
archive.InsertFile(fileStateFile);
}
void CIoman::SaveUserDevicesState(Framework::CZipArchiveWriter& archive)
{
auto deviceStateFile = new CXmlStateFile(STATE_USERDEVICES_FILENAME, STATE_USERDEVICES_DEVICESNODE);
auto devicesStateNode = deviceStateFile->GetRoot();
for(const auto& devicePair : m_userDevices)
{
auto deviceStateNode = new Framework::Xml::CNode(STATE_USERDEVICES_DEVICENODE, true);
deviceStateNode->InsertAttribute(Framework::Xml::CreateAttributeStringValue(STATE_USERDEVICES_DEVICENODE_NAMEATTRIBUTE, devicePair.first.c_str()));
deviceStateNode->InsertAttribute(Framework::Xml::CreateAttributeIntValue(STATE_USERDEVICES_DEVICENODE_DESCPTRATTRIBUTE, devicePair.second));
devicesStateNode->InsertNode(deviceStateNode);
}
archive.InsertFile(deviceStateFile);
}
void CIoman::LoadFilesState(Framework::CZipArchiveReader& archive)
{
std::experimental::erase_if(m_files,
[](const FileMapType::value_type& filePair) {
return (filePair.first != FID_STDOUT) && (filePair.first != FID_STDERR);
});
auto fileStateFile = CXmlStateFile(*archive.BeginReadFile(STATE_FILES_FILENAME));
auto fileStateNode = fileStateFile.GetRoot();
int32 maxFileId = FID_STDERR;
auto fileNodes = fileStateNode->SelectNodes(STATE_FILES_FILESNODE "/" STATE_FILES_FILENODE);
for(auto fileNode : fileNodes)
{
int32 id = 0, flags = 0, descPtr = 0;
std::string path;
if(!Framework::Xml::GetAttributeIntValue(fileNode, STATE_FILES_FILENODE_IDATTRIBUTE, &id)) break;
if(!Framework::Xml::GetAttributeStringValue(fileNode, STATE_FILES_FILENODE_PATHATTRIBUTE, &path)) break;
if(!Framework::Xml::GetAttributeIntValue(fileNode, STATE_FILES_FILENODE_FLAGSATTRIBUTE, &flags)) break;
if(!Framework::Xml::GetAttributeIntValue(fileNode, STATE_FILES_FILENODE_DESCPTRATTRIBUTE, &descPtr)) break;
FileInfo fileInfo;
fileInfo.flags = flags;
fileInfo.path = path;
fileInfo.descPtr = descPtr;
fileInfo.stream = (descPtr == 0) ? OpenInternal(flags, path.c_str()) : nullptr;
m_files[id] = std::move(fileInfo);
maxFileId = std::max(maxFileId, id);
}
m_nextFileHandle = maxFileId + 1;
}
void CIoman::LoadUserDevicesState(Framework::CZipArchiveReader& archive)
{
m_userDevices.clear();
auto deviceStateFile = CXmlStateFile(*archive.BeginReadFile(STATE_USERDEVICES_FILENAME));
auto deviceStateNode = deviceStateFile.GetRoot();
auto deviceNodes = deviceStateNode->SelectNodes(STATE_USERDEVICES_DEVICESNODE "/" STATE_USERDEVICES_DEVICENODE);
for(auto deviceNode : deviceNodes)
{
std::string name;
int32 descPtr = 0;
if(!Framework::Xml::GetAttributeStringValue(deviceNode, STATE_USERDEVICES_DEVICENODE_NAMEATTRIBUTE, &name)) break;
if(!Framework::Xml::GetAttributeIntValue(deviceNode, STATE_USERDEVICES_DEVICENODE_DESCPTRATTRIBUTE, &descPtr)) break;
m_userDevices[name] = descPtr;
}
}