Improved GameViewer

GameViewer use VFS.
Implemented be_t increment / decrement
Implemented se
Improved sys_fs syscalls.
This commit is contained in:
DH 2014-02-22 04:53:06 +02:00
parent d6fe398f79
commit 05184d2e71
13 changed files with 178 additions and 120 deletions

View File

@ -2,17 +2,14 @@
#include "Utilities/GNU.h"
#define se16(x) const_se_t<u16, x>::value
#define se32(x) const_se_t<u32, x>::value
#define se64(x) const_se_t<u64, x>::value
template<typename T, int size = sizeof(T)> struct se_t;
template<typename T> struct se_t<T, 1> { static __forceinline void func(T& dst, const T src) { (u8&)dst = (u8&)src; } };
template<typename T> struct se_t<T, 2> { static __forceinline void func(T& dst, const T src) { (u16&)dst = _byteswap_ushort((u16&)src); } };
template<typename T> struct se_t<T, 4> { static __forceinline void func(T& dst, const T src) { (u32&)dst = _byteswap_ulong((u32&)src); } };
template<typename T> struct se_t<T, 8> { static __forceinline void func(T& dst, const T src) { (u64&)dst = _byteswap_uint64((u64&)src); } };
template<typename T, s64 _value, int size = sizeof(T)> struct const_se_t;;
template<typename T, s64 _value, int size = sizeof(T)> struct const_se_t;
template<typename T, s64 _value> struct const_se_t<T, _value, 1>
{
static const T value = (T)_value;
@ -52,6 +49,8 @@ class be_t
T m_data;
public:
typedef T type;
be_t()
{
}
@ -91,6 +90,20 @@ public:
se_t<T>::func(m_data, value);
}
static be_t MakeFromLE(const T value)
{
be_t res;
res.FromLE(value);
return res;
}
static be_t MakeFromBE(const T value)
{
be_t res;
res.FromBE(value);
return res;
}
//template<typename T1>
operator const T() const
{
@ -154,4 +167,17 @@ public:
template<typename T1> bool operator < (const be_t<T1>& right) const { return (T1)ToLE() < right.ToLE(); }
template<typename T1> bool operator >= (const be_t<T1>& right) const { return (T1)ToLE() >= right.ToLE(); }
template<typename T1> bool operator <= (const be_t<T1>& right) const { return (T1)ToLE() <= right.ToLE(); }
be_t operator++ (int) { be_t res = *this; *this += 1; return res; }
be_t operator-- (int) { be_t res = *this; *this -= 1; return res; }
be_t& operator++ () { *this += 1; return *this; }
be_t& operator-- () { *this -= 1; return *this; }
};
template<typename T, typename T1, T1 value> struct _se : public const_se_t<T, value> {};
template<typename T, typename T1, T1 value> struct _se<be_t<T>, T1, value> : public const_se_t<T, value> {};
#define se(t, x) _se<decltype(t), decltype(x), x>::value
#define se16(x) _se<u16, decltype(x), x>::value
#define se32(x) _se<u32, decltype(x), x>::value
#define se64(x) _se<u64, decltype(x), x>::value

View File

@ -35,11 +35,11 @@ public:
struct ID
{
std::string m_name;
u8 m_attr;
u32 m_attr;
IDData* m_data;
template<typename T>
ID(const std::string& name, T* data, const u8 attr)
ID(const std::string& name, T* data, const u32 attr)
: m_name(name)
, m_attr(attr)
{
@ -97,7 +97,7 @@ public:
}
template<typename T>
ID_TYPE GetNewID(const std::string& name = "", T* data = nullptr, const u8 attr = 0)
ID_TYPE GetNewID(const std::string& name = "", T* data = nullptr, const u32 attr = 0)
{
std::lock_guard<std::mutex> lock(m_mtx_main);

View File

@ -2349,7 +2349,8 @@ private:
if(count == 1)
{
//RD[32+4*n : 32+4*n+3] = CR[4*n : 4*n+3];
CPU.GPR[rd] = (u64)CPU.GetCR(7 - n) << (n * 4);
u8 offset = n * 4;
CPU.GPR[rd] = (CPU.GPR[rd] & ~(0xf << offset)) | ((u32)CPU.GetCR(7 - n) << offset);
}
else
CPU.GPR[rd] = 0;

View File

@ -259,6 +259,8 @@ vfsDevice* VFS::GetDeviceLocal(const wxString& local_path, wxString& path) const
void VFS::Init(const wxString& path)
{
UnMountAll();
Array<VFSManagerEntry> entries;
SaveLoadDevices(entries, true);

View File

@ -56,7 +56,6 @@ const DirEntryInfo* vfsDir::Read()
void vfsDir::Close()
{
m_stream.reset();
return vfsDirBase::Close();
}
wxString vfsDir::GetPath() const
@ -66,5 +65,5 @@ wxString vfsDir::GetPath() const
bool vfsDir::IsOpened() const
{
return m_stream && m_stream->IsOpened() && vfsDirBase::IsOpened();
return m_stream && m_stream->IsOpened();
}

View File

@ -13,7 +13,7 @@ vfsDirBase::~vfsDirBase()
bool vfsDirBase::Open(const wxString& path)
{
if(!IsOpened())
if(IsOpened())
Close();
if(!IsExists(path))

View File

@ -2,9 +2,9 @@
enum DirEntryFlags
{
DirEntry_TypeDir = 0x0,
DirEntry_TypeFile = 0x1,
DirEntry_TypeMask = 0x1,
DirEntry_TypeDir = 0x1,
DirEntry_TypeFile = 0x2,
DirEntry_TypeMask = 0x3,
DirEntry_PermWritable = 0x20,
DirEntry_PermReadable = 0x40,
DirEntry_PermExecutable = 0x80,

View File

@ -23,12 +23,12 @@ bool vfsLocalDir::Open(const wxString& path)
wxString name;
for(bool is_ok = dir.GetFirst(&name); is_ok; is_ok = dir.GetNext(&name))
{
wxString dir_path = path + wxFILE_SEP_PATH + name;
wxString dir_path = path + name;
DirEntryInfo& info = m_entries[m_entries.Move(new DirEntryInfo())];
info.name = name;
info.flags |= wxDirExists(dir_path) ? DirEntry_TypeDir : DirEntry_TypeFile;
info.flags |= dir.Exists(dir_path) ? DirEntry_TypeDir : DirEntry_TypeFile;
if(wxIsWritable(dir_path)) info.flags |= DirEntry_PermWritable;
if(wxIsReadable(dir_path)) info.flags |= DirEntry_PermReadable;
if(wxIsExecutable(dir_path)) info.flags |= DirEntry_PermExecutable;

View File

@ -399,18 +399,18 @@ public:
extern MemoryBase Memory;
template<typename T>
template<typename T, typename AT = u32>
class mem_base_t
{
protected:
u32 m_addr;
AT m_addr;
public:
mem_base_t(u32 addr) : m_addr(addr)
mem_base_t(AT addr) : m_addr(addr)
{
}
__forceinline u32 GetAddr() const { return m_addr; }
__forceinline AT GetAddr() const { return m_addr; }
__forceinline bool IsGood() const
{
@ -433,16 +433,16 @@ public:
}
};
template<typename T>
class mem_ptr_t : public mem_base_t<T>
template<typename T, typename AT = u32>
class mem_ptr_t : public mem_base_t<T, AT>
{
public:
mem_ptr_t(u32 addr) : mem_base_t<T>(addr)
mem_ptr_t(AT addr) : mem_base_t<T, AT>(addr)
{
}
template<typename NT> operator mem_ptr_t<NT>&() { return (mem_ptr_t<NT>&)*this; }
template<typename NT> operator const mem_ptr_t<NT>&() const { return (const mem_ptr_t<NT>&)*this; }
template<typename NT> operator mem_ptr_t<NT, AT>&() { return (mem_ptr_t<NT, AT>&)*this; }
template<typename NT> operator const mem_ptr_t<NT, AT>&() const { return (const mem_ptr_t<NT, AT>&)*this; }
T* operator -> ()
{
@ -537,11 +537,11 @@ public:
bool operator <= (T* right) const { return (T*)&Memory[this->m_addr] <= right; }
};
template<>
class mem_ptr_t<void> : public mem_base_t<u8>
template<typename AT>
class mem_ptr_t<void, AT> : public mem_base_t<u8, AT>
{
public:
mem_ptr_t(u32 addr) : mem_base_t<u8>(addr)
mem_ptr_t(AT addr) : mem_base_t<u8, AT>(addr)
{
}
@ -570,10 +570,13 @@ template<typename T> static bool operator < (T* left, mem_ptr_t<T> right) { retu
template<typename T> static bool operator >= (T* left, mem_ptr_t<T> right) { return left >= (T*)&Memory[right.GetAddr()]; }
template<typename T> static bool operator <= (T* left, mem_ptr_t<T> right) { return left <= (T*)&Memory[right.GetAddr()]; }
template<typename T> class mem_t : public mem_base_t<T>
template<typename T, typename AT = u32>
class mem_beptr_t : public mem_ptr_t<T, be_t<AT>> {};
template<typename T, typename AT = u32> class mem_t : public mem_base_t<T, AT>
{
public:
mem_t(u32 addr) : mem_base_t<T>(addr)
mem_t(AT addr) : mem_base_t<T, AT>(addr)
{
}
@ -606,7 +609,7 @@ public:
mem_t& operator >>= (T right) { return *this = (*this) >> right; }
};
template<typename T> class mem_list_ptr_t : public mem_base_t<T>
template<typename T, typename AT=u32> class mem_list_ptr_t : public mem_base_t<T, AT>
{
public:
mem_list_ptr_t(u32 addr) : mem_base_t<T>(addr)
@ -687,18 +690,18 @@ struct _func_arg
};
template<typename T>
struct _func_arg<mem_base_t<T>>
struct _func_arg<mem_base_t<T, u32>>
{
__forceinline static u64 get_value(const mem_base_t<T> arg)
__forceinline static u64 get_value(const mem_base_t<T, u32> arg)
{
return arg.GetAddr();
}
};
template<typename T> struct _func_arg<mem_ptr_t<T>> : public _func_arg<mem_base_t<T>> {};
template<> struct _func_arg<mem_ptr_t<void>> : public _func_arg<mem_base_t<u8>> {};
template<typename T> struct _func_arg<mem_list_ptr_t<T>> : public _func_arg<mem_base_t<T>> {};
template<typename T> struct _func_arg<mem_t<T>> : public _func_arg<mem_base_t<T>> {};
template<typename T> struct _func_arg<mem_ptr_t<T, u32>> : public _func_arg<mem_base_t<T, u32>> {};
template<> struct _func_arg<mem_ptr_t<void, u32>> : public _func_arg<mem_base_t<u8, u32>> {};
template<typename T> struct _func_arg<mem_list_ptr_t<T, u32>> : public _func_arg<mem_base_t<T, u32>> {};
template<typename T> struct _func_arg<mem_t<T, u32>> : public _func_arg<mem_base_t<T, u32>> {};
template<typename T>
struct _func_arg<be_t<T>>
@ -912,10 +915,10 @@ public:
return m_ptr;
}
T operator [](int index)
{
return *(m_ptr + index);
}
T operator [](int index)
{
return *(m_ptr + index);
}
template<typename T1>
operator const mem_t<T1>() const
@ -935,10 +938,10 @@ public:
}
};
typedef mem_t<u8> mem8_t;
typedef mem_t<u16> mem16_t;
typedef mem_t<u32> mem32_t;
typedef mem_t<u64> mem64_t;
typedef mem_t<u8, u32> mem8_t;
typedef mem_t<u16, u32> mem16_t;
typedef mem_t<u32, u32> mem32_t;
typedef mem_t<u64, u32> mem64_t;
/*
typedef mem_ptr_t<be_t<u8>> mem8_ptr_t;
@ -952,7 +955,7 @@ typedef mem_list_ptr_t<u32> mem32_lptr_t;
typedef mem_list_ptr_t<u64> mem64_lptr_t;
*/
typedef mem_list_ptr_t<u8> mem8_ptr_t;
typedef mem_list_ptr_t<u16> mem16_ptr_t;
typedef mem_list_ptr_t<u32> mem32_ptr_t;
typedef mem_list_ptr_t<u64> mem64_ptr_t;
typedef mem_list_ptr_t<u8, u32> mem8_ptr_t;
typedef mem_list_ptr_t<u16, u32> mem16_ptr_t;
typedef mem_list_ptr_t<u32, u32> mem32_ptr_t;
typedef mem_list_ptr_t<u64, u32> mem64_ptr_t;

View File

@ -73,6 +73,18 @@ public:
return true;
}
template<typename T> bool CheckId(u32 id, T*& data, u32& attr)
{
ID* id_data;
if(!CheckID(id, id_data)) return false;
data = id_data->m_data->get<T>();
attr = id_data->m_attr;
return true;
}
bool CheckID(u32 id, ID*& _id) const;
template<typename T>

View File

@ -4,6 +4,12 @@
extern Module sys_fs;
enum
{
IDFlag_File = 1,
IDFlag_Dir = 2,
};
int cellFsOpen(u32 path_addr, int flags, mem32_t fd, mem32_t arg, u64 size)
{
const wxString& path = Memory.ReadString(path_addr);
@ -75,7 +81,7 @@ int cellFsOpen(u32 path_addr, int flags, mem32_t fd, mem32_t arg, u64 size)
return CELL_ENOENT;
}
fd = sys_fs.GetNewId(stream, flags);
fd = sys_fs.GetNewId(stream, IDFlag_File);
ConLog.Warning("*** cellFsOpen(path=\"%s\"): fd = %d", path.wx_str(), fd.GetValue());
return CELL_OK;
@ -148,7 +154,7 @@ int cellFsOpendir(u32 path_addr, mem32_t fd)
return CELL_ENOENT;
}
fd = sys_fs.GetNewId(dir);
fd = sys_fs.GetNewId(dir, IDFlag_Dir);
return CELL_OK;
}
@ -156,7 +162,7 @@ int cellFsReaddir(u32 fd, mem_ptr_t<CellFsDirent> dir, mem64_t nread)
{
sys_fs.Log("cellFsReaddir(fd=%d, dir_addr=0x%x, nread_addr=0x%x)", fd, dir.GetAddr(), nread.GetAddr());
vfsLocalDir* directory;
vfsDirBase* directory;
if(!sys_fs.CheckId(fd, directory))
return CELL_ESRCH;
if(!dir.IsGood() || !nread.IsGood())
@ -205,56 +211,36 @@ int cellFsStat(const u32 path_addr, mem_ptr_t<CellFsStat> sb)
sb->st_ctime_ = 0; //TODO
sb->st_blksize = 4096;
// Check if path is a mount point. (TODO: Add information in sb_addr)
for(u32 i=0; i<Emu.GetVFS().m_devices.GetCount(); ++i)
{
if(path.CmpNoCase(Emu.GetVFS().m_devices[i].GetPs3Path().RemoveLast(1)) == 0)
vfsDir dir(path);
if(dir.IsOpened())
{
sys_fs.Log("cellFsStat: \"%s\" is a mount point.", path.wx_str());
sb->st_mode |= CELL_FS_S_IFDIR;
return CELL_OK;
}
}
if (path == "/dev_bdvd/PS3_GAME/USRDIR")
{
sys_fs.Warning("cellFsStat: /dev_bdvd/PS3_GAME/USRDIR mount point hack");
sb->st_mode |= CELL_FS_S_IFDIR;
return CELL_OK;
}
// TODO: Temporary solution until vfsDir is implemented
wxString real_path;
Emu.GetVFS().GetDevice(path, real_path);
struct stat s;
if(stat(real_path.c_str(), &s) == 0)
{
if(s.st_mode & S_IFDIR)
vfsFile f(path);
if(f.IsOpened())
{
sb->st_mode |= CELL_FS_S_IFDIR;
}
else if(s.st_mode & S_IFREG)
{
vfsFile f(path);
sb->st_mode |= CELL_FS_S_IFREG;
sb->st_size = f.GetSize();
return CELL_OK;
}
}
else
{
sys_fs.Warning("cellFsStat: \"%s\" not found.", path.wx_str());
return CELL_ENOENT;
}
return CELL_OK;
sys_fs.Warning("cellFsStat: \"%s\" not found.", path.wx_str());
return CELL_ENOENT;
}
int cellFsFstat(u32 fd, mem_ptr_t<CellFsStat> sb)
{
sys_fs.Log("cellFsFstat(fd=%d, sb_addr: 0x%x)", fd, sb.GetAddr());
u32 attr;
vfsStream* file;
if(!sys_fs.CheckId(fd, file)) return CELL_ESRCH;
if(!sys_fs.CheckId(fd, file, attr) || attr != IDFlag_File) return CELL_ESRCH;
sb->st_mode =
CELL_FS_S_IRUSR | CELL_FS_S_IWUSR | CELL_FS_S_IXUSR |
@ -278,10 +264,12 @@ int cellFsMkdir(u32 path_addr, u32 mode)
const wxString& ps3_path = Memory.ReadString(path_addr);
sys_fs.Log("cellFsMkdir(path=\"%s\", mode=0x%x)", ps3_path.wx_str(), mode);
wxString localPath;
Emu.GetVFS().GetDevice(ps3_path, localPath);
if(wxDirExists(localPath)) return CELL_EEXIST;
if(!wxMkdir(localPath)) return CELL_EBUSY;
vfsDir dir;
if(dir.IsExists(ps3_path))
return CELL_EEXIST;
if(!dir.Create(ps3_path))
return CELL_EBUSY;
return CELL_OK;
}
@ -289,16 +277,30 @@ int cellFsRename(u32 from_addr, u32 to_addr)
{
const wxString& ps3_from = Memory.ReadString(from_addr);
const wxString& ps3_to = Memory.ReadString(to_addr);
wxString from;
wxString to;
Emu.GetVFS().GetDevice(ps3_from, from);
Emu.GetVFS().GetDevice(ps3_to, to);
sys_fs.Log("cellFsRename(from=\"%s\", to=\"%s\")", ps3_from.wx_str(), ps3_to.wx_str());
if(!wxFileExists(from)) return CELL_ENOENT;
if(wxFileExists(to)) return CELL_EEXIST;
if(!wxRenameFile(from, to)) return CELL_EBUSY; // (TODO: RenameFile(a,b) = CopyFile(a,b) + RemoveFile(a), therefore file "a" will not be removed if it is opened)
return CELL_OK;
{
vfsDir dir;
if(dir.IsExists(ps3_from))
{
if(!dir.Rename(ps3_from, ps3_to))
return CELL_EBUSY;
return CELL_OK;
}
}
{
vfsFile f;
if(f.Exists(ps3_from))
{
if(!f.Rename(ps3_from, ps3_to))
return CELL_EBUSY;
return CELL_OK;
}
}
return CELL_ENOENT;
}
int cellFsRmdir(u32 path_addr)
@ -306,10 +308,13 @@ int cellFsRmdir(u32 path_addr)
const wxString& ps3_path = Memory.ReadString(path_addr);
sys_fs.Log("cellFsRmdir(path=\"%s\")", ps3_path.wx_str());
wxString localPath;
Emu.GetVFS().GetDevice(ps3_path, localPath);
if(!wxDirExists(localPath)) return CELL_ENOENT;
if(!wxRmdir(localPath)) return CELL_EBUSY; // (TODO: Under certain conditions it is not able to delete the folder)
vfsDir d;
if(!d.IsExists(ps3_path))
return CELL_ENOENT;
if(!d.Remove(ps3_path))
return CELL_EBUSY;
return CELL_OK;
}
@ -318,9 +323,9 @@ int cellFsUnlink(u32 path_addr)
const wxString& ps3_path = Memory.ReadString(path_addr);
sys_fs.Warning("cellFsUnlink(path=\"%s\")", ps3_path.wx_str());
wxString localPath;
Emu.GetVFS().GetDevice(ps3_path, localPath);
wxRemoveFile(localPath);
//wxString localPath;
//Emu.GetVFS().GetDevice(ps3_path, localPath);
//wxRemoveFile(localPath);
return CELL_OK;
}
@ -338,8 +343,9 @@ int cellFsLseek(u32 fd, s64 offset, u32 whence, mem64_t pos)
return CELL_EINVAL;
}
u32 attr;
vfsStream* file;
if(!sys_fs.CheckId(fd, file)) return CELL_ESRCH;
if(!sys_fs.CheckId(fd, file, attr) || attr != IDFlag_File) return CELL_ESRCH;
pos = file->Seek(offset, seek_mode);
return CELL_OK;
}
@ -347,8 +353,9 @@ int cellFsLseek(u32 fd, s64 offset, u32 whence, mem64_t pos)
int cellFsFtruncate(u32 fd, u64 size)
{
sys_fs.Log("cellFsFtruncate(fd=%d, size=%lld)", fd, size);
u32 attr;
vfsStream* file;
if(!sys_fs.CheckId(fd, file)) return CELL_ESRCH;
if(!sys_fs.CheckId(fd, file, attr) || attr != IDFlag_File) return CELL_ESRCH;
u64 initialSize = file->GetSize();
if (initialSize < size)
@ -403,7 +410,10 @@ int cellFsTruncate(u32 path_addr, u64 size)
int cellFsFGetBlockSize(u32 fd, mem64_t sector_size, mem64_t block_size)
{
sys_fs.Log("cellFsFGetBlockSize(fd=%d, sector_size_addr: 0x%x, block_size_addr: 0x%x)", fd, sector_size.GetAddr(), block_size.GetAddr());
vfsStream* file;
if(!sys_fs.CheckId(fd, file)) return CELL_ESRCH;
sector_size = 4096; // ?
block_size = 4096; // ?

View File

@ -12,10 +12,10 @@ int sys_rwlock_create(mem32_t rw_lock_id, mem_ptr_t<sys_rwlock_attribute_t> attr
switch (attr->attr_protocol.ToBE())
{
case se32(SYS_SYNC_PRIORITY): sys_rwlock.Warning("TODO: SYS_SYNC_PRIORITY attr"); break;
case se32(SYS_SYNC_RETRY): sys_rwlock.Error("Invalid SYS_SYNC_RETRY attr"); break;
case se32(SYS_SYNC_PRIORITY_INHERIT): sys_rwlock.Warning("TODO: SYS_SYNC_PRIORITY_INHERIT attr"); break;
case se32(SYS_SYNC_FIFO): break;
case se(attr->attr_protocol, SYS_SYNC_PRIORITY): sys_rwlock.Warning("TODO: SYS_SYNC_PRIORITY attr"); break;
case se(attr->attr_protocol, SYS_SYNC_RETRY): sys_rwlock.Error("Invalid SYS_SYNC_RETRY attr"); break;
case se(attr->attr_protocol, SYS_SYNC_PRIORITY_INHERIT): sys_rwlock.Warning("TODO: SYS_SYNC_PRIORITY_INHERIT attr"); break;
case se(attr->attr_protocol, SYS_SYNC_FIFO): break;
default: return CELL_EINVAL;
}

View File

@ -8,7 +8,7 @@ GameViewer::GameViewer(wxWindow* parent) : wxListView(parent)
LoadSettings();
m_columns.Show(this);
m_path = wxGetCwd() + "\\dev_hdd0\\game\\"; //TODO
m_path = "/dev_hdd0/game/";
Connect(GetId(), wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(GameViewer::DClick));
@ -27,17 +27,18 @@ void GameViewer::DoResize(wxSize size)
void GameViewer::LoadGames()
{
if(!wxDirExists(m_path)) return;
vfsDir dir(m_path);
ConLog.Write("path: %s", m_path.wx_str());
if(!dir.IsOpened()) return;
m_games.Clear();
wxDir dir(m_path);
if(!dir.HasSubDirs()) return;
wxString buf;
for(bool ok = dir.GetFirst(&buf); ok; ok = dir.GetNext(&buf))
for(const DirEntryInfo* info = dir.Read(); info; info = dir.Read())
{
if(wxDirExists(m_path + buf))
m_games.Add(buf);
if(info->flags & DirEntry_TypeDir)
{
m_games.Add(info->name);
}
}
//ConLog.Write("path: %s", m_path.wx_str());
@ -49,8 +50,8 @@ void GameViewer::LoadPSF()
m_game_data.Clear();
for(uint i=0; i<m_games.GetCount(); ++i)
{
const wxString& path = m_path + m_games[i] + "\\PARAM.SFO";
vfsLocalFile f(nullptr);
const wxString& path = m_path + m_games[i] + "/PARAM.SFO";
vfsFile f;
if(!f.Open(path))
continue;
@ -70,9 +71,11 @@ void GameViewer::ShowData()
void GameViewer::Refresh()
{
Emu.GetVFS().Init(m_path);
LoadGames();
LoadPSF();
ShowData();
Emu.GetVFS().UnMountAll();
}
void GameViewer::SaveSettings()
@ -92,8 +95,10 @@ void GameViewer::DClick(wxListEvent& event)
const wxString& path = m_path + m_game_data[i].root;
Emu.GetVFS().Init(path);
wxString local_path;
Emu.Stop();
if(!Emu.BootGame(path.ToStdString()))
if(Emu.GetVFS().GetDevice(path, local_path) && !Emu.BootGame(local_path.ToStdString()))
{
ConLog.Error("Boot error: elf not found! [%s]", path.wx_str());
return;