Partial commit: Emu (the rest)

This commit is contained in:
Nekotekina 2016-04-14 01:59:00 +03:00
parent c7738b8b37
commit f8f067ca7c
28 changed files with 1043 additions and 4765 deletions

View file

@ -1,951 +0,0 @@
#pragma once
#include "CPUInstrTable.h"
class CPUDecoder
{
public:
virtual u32 DecodeMemory(const u32 address) = 0;
virtual ~CPUDecoder() = default;
};
template<typename TO>
class InstrCaller
{
public:
virtual ~InstrCaller<TO>() = default;
virtual void operator ()(TO* op, u32 code) const = 0;
virtual u32 operator [](u32) const
{
return 0;
}
};
template<typename TO>
class InstrBinder_0 : public InstrCaller<TO>
{
typedef void (TO::*func_t)();
func_t m_func;
public:
InstrBinder_0(func_t func)
: InstrCaller<TO>()
, m_func(func)
{
}
virtual void operator ()(TO* op, u32 code) const
{
(op->*m_func)();
}
};
template<typename TO, typename T1>
class InstrBinder_1 : public InstrCaller<TO>
{
typedef void (TO::*func_t)(T1);
func_t m_func;
const CodeFieldBase& m_arg_func_1;
public:
InstrBinder_1(func_t func, const CodeFieldBase& arg_func_1)
: InstrCaller<TO>()
, m_func(func)
, m_arg_func_1(arg_func_1)
{
}
virtual void operator ()(TO* op, u32 code) const
{
(op->*m_func)(static_cast<T1>(m_arg_func_1(code)));
}
};
template<typename TO, typename T1, typename T2>
class InstrBinder_2 : public InstrCaller<TO>
{
typedef void (TO::*func_t)(T1, T2);
func_t m_func;
const CodeFieldBase& m_arg_func_1;
const CodeFieldBase& m_arg_func_2;
public:
InstrBinder_2(func_t func, const CodeFieldBase& arg_func_1, const CodeFieldBase& arg_func_2)
: InstrCaller<TO>()
, m_func(func)
, m_arg_func_1(arg_func_1)
, m_arg_func_2(arg_func_2)
{
}
virtual void operator ()(TO* op, u32 code) const
{
(op->*m_func)(
static_cast<T1>(m_arg_func_1(code)),
static_cast<T2>(m_arg_func_2(code))
);
}
};
template<typename TO, typename T1, typename T2, typename T3>
class InstrBinder_3 : public InstrCaller<TO>
{
typedef void (TO::*func_t)(T1, T2, T3);
func_t m_func;
const CodeFieldBase& m_arg_func_1;
const CodeFieldBase& m_arg_func_2;
const CodeFieldBase& m_arg_func_3;
public:
InstrBinder_3(func_t func,
const CodeFieldBase& arg_func_1,
const CodeFieldBase& arg_func_2,
const CodeFieldBase& arg_func_3)
: InstrCaller<TO>()
, m_func(func)
, m_arg_func_1(arg_func_1)
, m_arg_func_2(arg_func_2)
, m_arg_func_3(arg_func_3)
{
}
virtual void operator ()(TO* op, u32 code) const
{
(op->*m_func)(
static_cast<T1>(m_arg_func_1(code)),
static_cast<T2>(m_arg_func_2(code)),
static_cast<T3>(m_arg_func_3(code))
);
}
};
template<typename TO, typename T1, typename T2, typename T3, typename T4>
class InstrBinder_4 : public InstrCaller<TO>
{
typedef void (TO::*func_t)(T1, T2, T3, T4);
func_t m_func;
const CodeFieldBase& m_arg_func_1;
const CodeFieldBase& m_arg_func_2;
const CodeFieldBase& m_arg_func_3;
const CodeFieldBase& m_arg_func_4;
public:
InstrBinder_4(func_t func,
const CodeFieldBase& arg_func_1,
const CodeFieldBase& arg_func_2,
const CodeFieldBase& arg_func_3,
const CodeFieldBase& arg_func_4)
: InstrCaller<TO>()
, m_func(func)
, m_arg_func_1(arg_func_1)
, m_arg_func_2(arg_func_2)
, m_arg_func_3(arg_func_3)
, m_arg_func_4(arg_func_4)
{
}
virtual void operator ()(TO* op, u32 code) const
{
(op->*m_func)(
static_cast<T1>(m_arg_func_1(code)),
static_cast<T2>(m_arg_func_2(code)),
static_cast<T3>(m_arg_func_3(code)),
static_cast<T4>(m_arg_func_4(code))
);
}
};
template<typename TO, typename T1, typename T2, typename T3, typename T4, typename T5>
class InstrBinder_5 : public InstrCaller<TO>
{
typedef void (TO::*func_t)(T1, T2, T3, T4, T5);
func_t m_func;
const CodeFieldBase& m_arg_func_1;
const CodeFieldBase& m_arg_func_2;
const CodeFieldBase& m_arg_func_3;
const CodeFieldBase& m_arg_func_4;
const CodeFieldBase& m_arg_func_5;
public:
InstrBinder_5(func_t func,
const CodeFieldBase& arg_func_1,
const CodeFieldBase& arg_func_2,
const CodeFieldBase& arg_func_3,
const CodeFieldBase& arg_func_4,
const CodeFieldBase& arg_func_5)
: InstrCaller<TO>()
, m_func(func)
, m_arg_func_1(arg_func_1)
, m_arg_func_2(arg_func_2)
, m_arg_func_3(arg_func_3)
, m_arg_func_4(arg_func_4)
, m_arg_func_5(arg_func_5)
{
}
virtual void operator ()(TO* op, u32 code) const
{
(op->*m_func)(
static_cast<T1>(m_arg_func_1(code)),
static_cast<T2>(m_arg_func_2(code)),
static_cast<T3>(m_arg_func_3(code)),
static_cast<T4>(m_arg_func_4(code)),
static_cast<T5>(m_arg_func_5(code))
);
}
};
template<typename TO, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
class InstrBinder_6 : public InstrCaller<TO>
{
typedef void (TO::*func_t)(T1, T2, T3, T4, T5, T6);
func_t m_func;
const CodeFieldBase& m_arg_func_1;
const CodeFieldBase& m_arg_func_2;
const CodeFieldBase& m_arg_func_3;
const CodeFieldBase& m_arg_func_4;
const CodeFieldBase& m_arg_func_5;
const CodeFieldBase& m_arg_func_6;
public:
InstrBinder_6(func_t func,
const CodeFieldBase& arg_func_1,
const CodeFieldBase& arg_func_2,
const CodeFieldBase& arg_func_3,
const CodeFieldBase& arg_func_4,
const CodeFieldBase& arg_func_5,
const CodeFieldBase& arg_func_6)
: InstrCaller<TO>()
, m_func(func)
, m_arg_func_1(arg_func_1)
, m_arg_func_2(arg_func_2)
, m_arg_func_3(arg_func_3)
, m_arg_func_4(arg_func_4)
, m_arg_func_5(arg_func_5)
, m_arg_func_6(arg_func_6)
{
}
virtual void operator ()(TO* op, u32 code) const
{
(op->*m_func)(
static_cast<T1>(m_arg_func_1(code)),
static_cast<T2>(m_arg_func_2(code)),
static_cast<T3>(m_arg_func_3(code)),
static_cast<T4>(m_arg_func_4(code)),
static_cast<T5>(m_arg_func_5(code)),
static_cast<T6>(m_arg_func_6(code))
);
}
};
template<typename TO>
InstrCaller<TO>* instr_bind(void (TO::*func)())
{
return new InstrBinder_0<TO>(func);
}
template<typename TO, typename T1>
InstrCaller<TO>* instr_bind(void (TO::*func)(T1), const CodeFieldBase& arg_func_1)
{
return new InstrBinder_1<TO, T1>(func, arg_func_1);
}
template<typename TO, typename T1, typename T2>
InstrCaller<TO>* instr_bind(void (TO::*func)(T1, T2),
const CodeFieldBase& arg_func_1,
const CodeFieldBase& arg_func_2)
{
return new InstrBinder_2<TO, T1, T2>(func, arg_func_1, arg_func_2);
}
template<typename TO, typename T1, typename T2, typename T3>
InstrCaller<TO>* instr_bind(void (TO::*func)(T1, T2, T3),
const CodeFieldBase& arg_func_1,
const CodeFieldBase& arg_func_2,
const CodeFieldBase& arg_func_3)
{
return new InstrBinder_3<TO, T1, T2, T3>(func, arg_func_1, arg_func_2, arg_func_3);
}
template<typename TO, typename T1, typename T2, typename T3, typename T4>
InstrCaller<TO>* instr_bind(void (TO::*func)(T1, T2, T3, T4),
const CodeFieldBase& arg_func_1,
const CodeFieldBase& arg_func_2,
const CodeFieldBase& arg_func_3,
const CodeFieldBase& arg_func_4)
{
return new InstrBinder_4<TO, T1, T2, T3, T4>(func, arg_func_1, arg_func_2, arg_func_3, arg_func_4);
}
template<typename TO, typename T1, typename T2, typename T3, typename T4, typename T5>
InstrCaller<TO>* instr_bind(void (TO::*func)(T1, T2, T3, T4, T5),
const CodeFieldBase& arg_func_1,
const CodeFieldBase& arg_func_2,
const CodeFieldBase& arg_func_3,
const CodeFieldBase& arg_func_4,
const CodeFieldBase& arg_func_5)
{
return new InstrBinder_5<TO, T1, T2, T3, T4, T5>(func, arg_func_1, arg_func_2, arg_func_3, arg_func_4, arg_func_5);
}
template<typename TO, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
InstrCaller<TO>* instr_bind(void (TO::*func)(T1, T2, T3, T4, T5, T6),
const CodeFieldBase& arg_func_1,
const CodeFieldBase& arg_func_2,
const CodeFieldBase& arg_func_3,
const CodeFieldBase& arg_func_4,
const CodeFieldBase& arg_func_5,
const CodeFieldBase& arg_func_6)
{
return new InstrBinder_6<TO, T1, T2, T3, T4, T5, T6>(func, arg_func_1, arg_func_2, arg_func_3, arg_func_4, arg_func_5, arg_func_6);
}
template<typename TO>
class InstrBase : public InstrCaller<TO>
{
protected:
std::string m_name;
const u32 m_opcode;
CodeFieldBase** m_args;
const uint m_args_count;
public:
InstrBase(const std::string& name, int opcode, uint args_count)
: InstrCaller<TO>()
, m_name(name)
, m_opcode(opcode)
, m_args_count(args_count)
, m_args(args_count ? new CodeFieldBase*[args_count] : nullptr)
{
std::transform(
name.begin(),
name.end(),
m_name.begin(),
[](const char &a)
{
char b = tolower(a);
if (b == '_') b = '.';
return b;
});
}
InstrBase(const InstrBase &source)
: InstrCaller<TO>(source)
, m_name(source.m_name)
, m_opcode(source.m_opcode)
, m_args_count(source.m_args_count)
, m_args(source.m_args_count ? new CodeFieldBase*[source.m_args_count] : nullptr)
{
for(uint i = 0; i < source.m_args_count; ++i)
m_args[i] = source.m_args[i];
}
virtual ~InstrBase()
{
if (m_args) {
// m_args contains pointers to statically allocated CodeFieldBase objects
// We shouldn't call delete on these, they aren't allocated with new
// The m_args array itself, however, should be deleted
delete[] m_args;
}
}
force_inline const std::string& GetName() const
{
return m_name;
}
force_inline const uint GetArgCount() const
{
return m_args_count;
}
force_inline const CodeFieldBase& GetArg(uint index) const
{
assert(index < m_args_count);
return *m_args[index];
}
void operator ()(TO* op, u32 code) const
{
decode(op, code);
}
u32 operator()(const std::vector<u32>& args) const
{
return encode(args);
}
virtual void decode(TO* op, u32 code) const=0;
virtual u32 encode(const std::vector<u32>& args) const=0;
};
template<int _count, typename TO>
class InstrList : public InstrCaller<TO>
{
public:
static const int count = _count;
protected:
const CodeFieldBase& m_func;
InstrCaller<TO>* m_instrs[count];
InstrBase<TO>* m_instrs_info[count];
InstrCaller<TO>* m_error_func;
InstrCaller<TO>* m_parent;
int m_opcode;
public:
InstrList(const CodeFieldBase& func, InstrCaller<TO>* error_func)
: InstrCaller<TO>()
, m_func(func)
, m_error_func(error_func)
, m_parent(nullptr)
, m_opcode(-1)
{
for(int i=0; i<count; ++i)
{
m_instrs[i] = error_func;
}
memset(m_instrs_info, 0, sizeof(InstrBase<TO>*) * count);
}
virtual ~InstrList()
{
bool deletedErrorFunc = false;
// Clean up m_instrs
for(int i = 0; i < count; ++i)
{
InstrCaller<TO>* deleteMe = m_instrs[i];
if (deleteMe) { // deleteMe will be a nullptr if we've already deleted it through another reference
// Remove any instances of pointers to this instruction caller from our m_instrs list
m_instrs[i] = nullptr;
for (int j = i + 1; j < count; j++) {
if (m_instrs[j] == deleteMe) {
m_instrs[j] = nullptr;
}
}
// If we're deleting the error handler here, remember it so we don't try to delete it again later
if (deleteMe == m_error_func) {
deletedErrorFunc = true;
}
// Delete the instruction caller
delete deleteMe;
}
}
// Clean up m_instrs_info
for (int i = 0; i < count; ++i)
{
InstrBase<TO>* deleteMe = m_instrs_info[i];
if (deleteMe) {
m_instrs_info[i] = nullptr;
for (int j = i + 1; j < count; j++) {
if (m_instrs_info[j] == deleteMe) {
m_instrs[j] = nullptr;
}
}
delete deleteMe;
}
}
// If we haven't already deleted our error handler, and we have one, then delete it now
if (!deletedErrorFunc && m_error_func)
{
delete m_error_func;
}
}
void set_parent(InstrCaller<TO>* parent, int opcode)
{
m_opcode = opcode;
m_parent = parent;
}
InstrCaller<TO>* get_parent() const
{
return m_parent;
}
u32 get_opcode() const
{
return m_opcode;
}
void set_error_func(InstrCaller<TO>* error_func)
{
for(int i=0; i<count; ++i)
{
if(m_instrs[i] == m_error_func || !m_instrs[i])
{
m_instrs[i] = error_func;
}
}
m_error_func = error_func;
}
void set_instr(uint pos, InstrCaller<TO>* func, InstrBase<TO>* info = nullptr)
{
assert(pos < count);
m_instrs[pos] = func;
m_instrs_info[pos] = info;
}
InstrCaller<TO>* get_instr(int pos) const
{
assert(pos < count);
return m_instrs[pos];
}
InstrBase<TO>* get_instr_info(int pos) const
{
assert(pos < count);
return m_instrs_info[pos];
}
u32 encode(u32 entry) const
{
return m_func[entry] | (m_parent ? (*m_parent)[m_opcode] : 0);
}
void decode(TO* op, u32 entry, u32 code) const
{
(*m_instrs[entry])(op, code);
}
virtual void operator ()(TO* op, u32 code) const
{
decode(op, m_func(code) & (count - 1), code);
}
virtual u32 operator [](u32 entry) const
{
return encode(entry);
}
};
template<int count1, int count2, typename TO>
static InstrList<count2, TO>* connect_list(InstrList<count1, TO>* parent, InstrList<count2, TO>* child, int opcode)
{
parent->set_instr(opcode, child);
child->set_parent(parent, opcode);
return child;
}
template<int count1, int count2, typename TO>
static InstrList<count2, TO>* connect_list(InstrList<count1, TO>* parent, InstrList<count2, TO>* child)
{
parent->set_error_func(child);
child->set_parent(parent->get_parent(), parent->get_opcode());
return child;
}
template<typename TO, int opcode, int count>
class Instr0 : public InstrBase<TO>
{
InstrList<count, TO>& m_list;
public:
Instr0(InstrList<count, TO>* list, const std::string& name,
void (TO::*func)())
: InstrBase<TO>(name, opcode, 0)
, m_list(*list)
{
m_list.set_instr(opcode, instr_bind(func), this);
}
virtual void decode(TO* op, u32 code) const
{
m_list.decode(op, opcode, code);
}
virtual u32 encode(const std::vector<u32>& args) const
{
assert(args.size() == InstrBase<TO>::m_args_count);
return m_list.encode(opcode);
}
u32 encode() const
{
return m_list.encode(opcode);
}
u32 operator()() const
{
return encode();
}
};
template<typename TO, int opcode, int count, typename T1>
class Instr1 : public InstrBase<TO>
{
InstrList<count, TO>& m_list;
public:
Instr1(InstrList<count, TO>* list, const std::string& name,
void (TO::*func)(T1),
CodeFieldBase& arg_1)
: InstrBase<TO>(name, opcode, 1)
, m_list(*list)
{
InstrBase<TO>::m_args[0] = &arg_1;
m_list.set_instr(opcode, instr_bind(func, arg_1), this);
}
virtual void decode(TO* op, u32 code) const
{
m_list.decode(op, opcode, code);
}
virtual u32 encode(const std::vector<u32>& args) const
{
assert(args.size() == InstrBase<TO>::m_args_count);
return m_list.encode(opcode) | (*InstrBase<TO>::m_args[0])[args[0]];
}
u32 encode(T1 a1) const
{
return m_list.encode(opcode) | (*InstrBase<TO>::m_args[0])[a1];
}
u32 operator()(T1 a1) const
{
return encode(a1);
}
};
template<typename TO, int opcode, int count, typename T1, typename T2>
class Instr2 : public InstrBase<TO>
{
InstrList<count, TO>& m_list;
public:
Instr2(InstrList<count, TO>* list, const std::string& name,
void (TO::*func)(T1, T2),
CodeFieldBase& arg_1,
CodeFieldBase& arg_2)
: InstrBase<TO>(name, opcode, 2)
, m_list(*list)
{
InstrBase<TO>::m_args[0] = &arg_1;
InstrBase<TO>::m_args[1] = &arg_2;
m_list.set_instr(opcode, instr_bind(func, arg_1, arg_2), this);
}
virtual void decode(TO* op, u32 code) const
{
m_list.decode(op, opcode, code);
}
virtual u32 encode(const std::vector<u32>& args) const
{
assert(args.size() == InstrBase<TO>::m_args_count);
return m_list.encode(opcode) | (*InstrBase<TO>::m_args[0])[args[0]] | (*InstrBase<TO>::m_args[1])[args[1]];
}
u32 encode(T1 a1, T2 a2) const
{
return m_list.encode(opcode) | (*InstrBase<TO>::m_args[0])[a1] | (*InstrBase<TO>::m_args[1])[a2];
}
u32 operator()(T1 a1, T2 a2) const
{
return encode(a1, a2);
}
};
template<typename TO, int opcode, int count, typename T1, typename T2, typename T3>
class Instr3 : public InstrBase<TO>
{
InstrList<count, TO>& m_list;
public:
Instr3(InstrList<count, TO>* list, const std::string& name,
void (TO::*func)(T1, T2, T3),
CodeFieldBase& arg_1,
CodeFieldBase& arg_2,
CodeFieldBase& arg_3)
: InstrBase<TO>(name, opcode, 3)
, m_list(*list)
{
InstrBase<TO>::m_args[0] = &arg_1;
InstrBase<TO>::m_args[1] = &arg_2;
InstrBase<TO>::m_args[2] = &arg_3;
m_list.set_instr(opcode, instr_bind(func, arg_1, arg_2, arg_3), this);
}
virtual void decode(TO* op, u32 code) const
{
m_list.decode(op, opcode, code);
}
virtual u32 encode(const std::vector<u32>& args) const
{
assert(args.size() == InstrBase<TO>::m_args_count);
return m_list.encode(opcode) | (*InstrBase<TO>::m_args[0])[args[0]] | (*InstrBase<TO>::m_args[1])[args[1]] | (*InstrBase<TO>::m_args[2])[args[2]];
}
u32 encode(T1 a1, T2 a2, T3 a3) const
{
return m_list.encode(opcode) | (*InstrBase<TO>::m_args[0])[a1] | (*InstrBase<TO>::m_args[1])[a2] | (*InstrBase<TO>::m_args[2])[a3];
}
u32 operator()(T1 a1, T2 a2, T3 a3) const
{
return encode(a1, a2, a3);
}
};
template<typename TO, int opcode, int count, typename T1, typename T2, typename T3, typename T4>
class Instr4 : public InstrBase<TO>
{
InstrList<count, TO>& m_list;
public:
Instr4(InstrList<count, TO>* list, const std::string& name,
void (TO::*func)(T1, T2, T3, T4),
CodeFieldBase& arg_1,
CodeFieldBase& arg_2,
CodeFieldBase& arg_3,
CodeFieldBase& arg_4)
: InstrBase<TO>(name, opcode, 4)
, m_list(*list)
{
InstrBase<TO>::m_args[0] = &arg_1;
InstrBase<TO>::m_args[1] = &arg_2;
InstrBase<TO>::m_args[2] = &arg_3;
InstrBase<TO>::m_args[3] = &arg_4;
m_list.set_instr(opcode, instr_bind(func, arg_1, arg_2, arg_3, arg_4), this);
}
virtual void decode(TO* op, u32 code) const
{
m_list.decode(op, opcode, code);
}
virtual u32 encode(const std::vector<u32>& args) const
{
assert(args.size() == InstrBase<TO>::m_args_count);
return m_list.encode(opcode) |
(*InstrBase<TO>::m_args[0])[args[0]] |
(*InstrBase<TO>::m_args[1])[args[1]] |
(*InstrBase<TO>::m_args[2])[args[2]] |
(*InstrBase<TO>::m_args[3])[args[3]];
}
u32 encode(T1 a1, T2 a2, T3 a3, T4 a4) const
{
return m_list.encode(opcode) |
(*InstrBase<TO>::m_args[0])[a1] |
(*InstrBase<TO>::m_args[1])[a2] |
(*InstrBase<TO>::m_args[2])[a3] |
(*InstrBase<TO>::m_args[3])[a4];
}
u32 operator()(T1 a1, T2 a2, T3 a3, T4 a4) const
{
return encode(a1, a2, a3, a4);
}
};
template<typename TO, int opcode, int count, typename T1, typename T2, typename T3, typename T4, typename T5>
class Instr5 : public InstrBase<TO>
{
InstrList<count, TO>& m_list;
public:
Instr5(InstrList<count, TO>* list, const std::string& name,
void (TO::*func)(T1, T2, T3, T4, T5),
CodeFieldBase& arg_1,
CodeFieldBase& arg_2,
CodeFieldBase& arg_3,
CodeFieldBase& arg_4,
CodeFieldBase& arg_5)
: InstrBase<TO>(name, opcode, 5)
, m_list(*list)
{
InstrBase<TO>::m_args[0] = &arg_1;
InstrBase<TO>::m_args[1] = &arg_2;
InstrBase<TO>::m_args[2] = &arg_3;
InstrBase<TO>::m_args[3] = &arg_4;
InstrBase<TO>::m_args[4] = &arg_5;
m_list.set_instr(opcode, instr_bind(func, arg_1, arg_2, arg_3, arg_4, arg_5), this);
}
virtual void decode(TO* op, u32 code) const
{
m_list.decode(op, opcode, code);
}
virtual u32 encode(const std::vector<u32>& args) const
{
assert(args.size() == InstrBase<TO>::m_args_count);
return m_list.encode(opcode) |
(*InstrBase<TO>::m_args[0])[args[0]] |
(*InstrBase<TO>::m_args[1])[args[1]] |
(*InstrBase<TO>::m_args[2])[args[2]] |
(*InstrBase<TO>::m_args[3])[args[3]] |
(*InstrBase<TO>::m_args[4])[args[4]];
}
u32 encode(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) const
{
return m_list.encode(opcode) |
(*InstrBase<TO>::m_args[0])[a1] |
(*InstrBase<TO>::m_args[1])[a2] |
(*InstrBase<TO>::m_args[2])[a3] |
(*InstrBase<TO>::m_args[3])[a4] |
(*InstrBase<TO>::m_args[4])[a5];
}
u32 operator()(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) const
{
return encode(a1, a2, a3, a4, a5);
}
};
template<typename TO, int opcode, int count, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
class Instr6 : public InstrBase<TO>
{
InstrList<count, TO>& m_list;
public:
Instr6(InstrList<count, TO>* list, const std::string& name,
void (TO::*func)(T1, T2, T3, T4, T5, T6),
CodeFieldBase& arg_1,
CodeFieldBase& arg_2,
CodeFieldBase& arg_3,
CodeFieldBase& arg_4,
CodeFieldBase& arg_5,
CodeFieldBase& arg_6)
: InstrBase<TO>(name, opcode, 6)
, m_list(*list)
{
InstrBase<TO>::m_args[0] = &arg_1;
InstrBase<TO>::m_args[1] = &arg_2;
InstrBase<TO>::m_args[2] = &arg_3;
InstrBase<TO>::m_args[3] = &arg_4;
InstrBase<TO>::m_args[4] = &arg_5;
InstrBase<TO>::m_args[5] = &arg_6;
m_list.set_instr(opcode, instr_bind(func, arg_1, arg_2, arg_3, arg_4, arg_5, arg_6), this);
}
virtual void decode(TO* op, u32 code) const
{
m_list.decode(op, opcode, code);
}
virtual u32 encode(const std::vector<u32>& args) const
{
assert(args.size() == InstrBase<TO>::m_args_count);
return m_list.encode(opcode) |
(*InstrBase<TO>::m_args[0])[args[0]] |
(*InstrBase<TO>::m_args[1])[args[1]] |
(*InstrBase<TO>::m_args[2])[args[2]] |
(*InstrBase<TO>::m_args[3])[args[3]] |
(*InstrBase<TO>::m_args[4])[args[4]] |
(*InstrBase<TO>::m_args[5])[args[5]];
}
u32 encode(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T5 a6) const
{
return m_list.encode(opcode) |
(*InstrBase<TO>::m_args[0])[a1] |
(*InstrBase<TO>::m_args[1])[a2] |
(*InstrBase<TO>::m_args[2])[a3] |
(*InstrBase<TO>::m_args[3])[a4] |
(*InstrBase<TO>::m_args[4])[a5] |
(*InstrBase<TO>::m_args[5])[a6];
}
u32 operator()(T1 a1, T2 a2, T3 a3, T4 a4, T4 a5, T4 a6) const
{
return encode(a1, a2, a3, a4, a5, a6);
}
};
template<int opcode, typename TO, int count>
static Instr0<TO, opcode, count>& make_instr(InstrList<count, TO>* list, const std::string& name, void (TO::*func)())
{
return *new Instr0<TO, opcode, count>(list, name, func);
}
template<int opcode, typename TO, int count, typename T1>
static Instr1<TO, opcode, count, T1>& make_instr(InstrList<count, TO>* list, const std::string& name,
void (TO::*func)(T1),
CodeFieldBase& arg_1)
{
return *new Instr1<TO, opcode, count, T1>(list, name, func, arg_1);
}
template<int opcode, typename TO, int count, typename T1, typename T2>
static Instr2<TO, opcode, count, T1, T2>& make_instr(InstrList<count, TO>* list, const std::string& name,
void (TO::*func)(T1, T2),
CodeFieldBase& arg_1,
CodeFieldBase& arg_2)
{
return *new Instr2<TO, opcode, count, T1, T2>(list, name, func, arg_1, arg_2);
}
template<int opcode, typename TO, int count, typename T1, typename T2, typename T3>
static Instr3<TO, opcode, count, T1, T2, T3>& make_instr(InstrList<count, TO>* list, const std::string& name,
void (TO::*func)(T1, T2, T3),
CodeFieldBase& arg_1,
CodeFieldBase& arg_2,
CodeFieldBase& arg_3)
{
return *new Instr3<TO, opcode, count, T1, T2, T3>(list, name, func, arg_1, arg_2, arg_3);
}
template<int opcode, typename TO, int count, typename T1, typename T2, typename T3, typename T4>
static Instr4<TO, opcode, count, T1, T2, T3, T4>& make_instr(InstrList<count, TO>* list, const std::string& name,
void (TO::*func)(T1, T2, T3, T4),
CodeFieldBase& arg_1,
CodeFieldBase& arg_2,
CodeFieldBase& arg_3,
CodeFieldBase& arg_4)
{
return *new Instr4<TO, opcode, count, T1, T2, T3, T4>(list, name, func, arg_1, arg_2, arg_3, arg_4);
}
template<int opcode, typename TO, int count, typename T1, typename T2, typename T3, typename T4, typename T5>
static Instr5<TO, opcode, count, T1, T2, T3, T4, T5>& make_instr(InstrList<count, TO>* list, const std::string& name,
void (TO::*func)(T1, T2, T3, T4, T5),
CodeFieldBase& arg_1,
CodeFieldBase& arg_2,
CodeFieldBase& arg_3,
CodeFieldBase& arg_4,
CodeFieldBase& arg_5)
{
return *new Instr5<TO, opcode, count, T1, T2, T3, T4, T5>(list, name, func, arg_1, arg_2, arg_3, arg_4, arg_5);
}
template<int opcode, typename TO, int count, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
static Instr6<TO, opcode, count, T1, T2, T3, T4, T5, T6>& make_instr(InstrList<count, TO>* list, const std::string& name,
void (TO::*func)(T1, T2, T3, T4, T5, T6),
CodeFieldBase& arg_1,
CodeFieldBase& arg_2,
CodeFieldBase& arg_3,
CodeFieldBase& arg_4,
CodeFieldBase& arg_5,
CodeFieldBase& arg_6)
{
return *new Instr6<TO, opcode, count, T1, T2, T3, T4, T5, T6>(list, name, func, arg_1, arg_2, arg_3, arg_4, arg_5, arg_6);
}

View file

@ -51,11 +51,14 @@ protected:
{
}
virtual u32 DisAsmBranchTarget(const s32 imm)=0;
virtual u32 DisAsmBranchTarget(const s32 imm) = 0;
std::string FixOp(std::string op)
{
op.append(std::max<int>(10 - (int)op.length(), 0),' ');
op.resize(std::max<std::size_t>(op.length(), 10), ' ');
return op;
}
public:
virtual u32 disasm(u32 pc) = 0;
};

View file

@ -1,34 +0,0 @@
#pragma once
template<uint size, typename T> force_inline static T sign(const T value)
{
static_assert(size > 0 && size < sizeof(T) * 8, "Bad sign size");
if(value & (T(1) << (size - 1)))
{
return value - (T(1) << size);
}
return value;
}
class CodeFieldBase
{
public:
u32 m_type;
public:
CodeFieldBase(u32 type) : m_type(type)
{
}
virtual u32 operator ()(u32 data) const=0;
virtual void operator()(u32& data, u32 value) const=0;
virtual u32 operator[](u32 value) const
{
u32 result = 0;
(*this)(result, value);
return result;
}
};

View file

@ -1,14 +1,16 @@
#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "CPUDecoder.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/Cell/SPUThread.h"
#include "Emu/Cell/RawSPUThread.h"
#include "Emu/ARMv7/ARMv7Thread.h"
#include "CPUThread.h"
thread_local CPUThread* g_tls_current_cpu_thread = nullptr;
thread_local cpu_thread* g_tls_current_cpu_thread = nullptr;
void CPUThread::on_task()
void cpu_thread::on_task()
{
g_tls_current_cpu_thread = this;
@ -16,13 +18,13 @@ void CPUThread::on_task()
std::unique_lock<std::mutex> lock(mutex);
// check thread status
while (is_alive())
// Check thread status
while (!(state & cpu_state::exit))
{
CHECK_EMU_STATUS;
// check stop status
if (!is_stopped())
if (!(state & cpu_state::stop))
{
if (lock) lock.unlock();
@ -30,26 +32,17 @@ void CPUThread::on_task()
{
cpu_task();
}
catch (CPUThreadReturn)
catch (cpu_state _s)
{
;
state += _s;
}
catch (CPUThreadStop)
catch (const std::exception&)
{
m_state |= CPU_STATE_STOPPED;
}
catch (CPUThreadExit)
{
m_state |= CPU_STATE_DEAD;
break;
}
catch (...)
{
dump_info();
LOG_NOTICE(GENERAL, "\n%s", dump());
throw;
}
m_state &= ~CPU_STATE_RETURN;
state -= cpu_state::ret;
continue;
}
@ -63,201 +56,7 @@ void CPUThread::on_task()
}
}
CPUThread::CPUThread(CPUThreadType type, const std::string& name)
: m_id(idm::get_last_id())
, m_type(type)
, m_name(name)
{
}
CPUThread::~CPUThread()
{
Emu.SendDbgCommand(DID_REMOVE_THREAD, this);
}
std::string CPUThread::get_name() const
{
return m_name;
}
bool CPUThread::is_paused() const
{
return (m_state & CPU_STATE_PAUSED) != 0 || Emu.IsPaused();
}
void CPUThread::dump_info() const
{
if (!Emu.IsStopped())
{
LOG_NOTICE(GENERAL, "%s", RegsToString());
}
}
void CPUThread::run()
{
Emu.SendDbgCommand(DID_START_THREAD, this);
init_stack();
init_regs();
do_run();
Emu.SendDbgCommand(DID_STARTED_THREAD, this);
}
void CPUThread::pause()
{
Emu.SendDbgCommand(DID_PAUSE_THREAD, this);
m_state |= CPU_STATE_PAUSED;
Emu.SendDbgCommand(DID_PAUSED_THREAD, this);
}
void CPUThread::resume()
{
Emu.SendDbgCommand(DID_RESUME_THREAD, this);
{
// lock for reliable notification
std::lock_guard<std::mutex> lock(mutex);
m_state &= ~CPU_STATE_PAUSED;
cv.notify_one();
}
Emu.SendDbgCommand(DID_RESUMED_THREAD, this);
}
void CPUThread::stop()
{
Emu.SendDbgCommand(DID_STOP_THREAD, this);
if (is_current())
{
throw CPUThreadStop{};
}
else
{
// lock for reliable notification
std::lock_guard<std::mutex> lock(mutex);
m_state |= CPU_STATE_STOPPED;
cv.notify_one();
}
Emu.SendDbgCommand(DID_STOPED_THREAD, this);
}
void CPUThread::exec()
{
Emu.SendDbgCommand(DID_EXEC_THREAD, this);
m_state &= ~CPU_STATE_STOPPED;
{
// lock for reliable notification
std::lock_guard<std::mutex> lock(mutex);
cv.notify_one();
}
}
void CPUThread::exit()
{
m_state |= CPU_STATE_DEAD;
if (!is_current())
{
// lock for reliable notification
std::lock_guard<std::mutex> lock(mutex);
cv.notify_one();
}
}
void CPUThread::step()
{
if (m_state.atomic_op([](u64& state) -> bool
{
const bool was_paused = (state & CPU_STATE_PAUSED) != 0;
state |= CPU_STATE_STEP;
state &= ~CPU_STATE_PAUSED;
return was_paused;
}))
{
if (is_current()) return;
// lock for reliable notification (only if PAUSE was removed)
std::lock_guard<std::mutex> lock(mutex);
cv.notify_one();
}
}
void CPUThread::sleep()
{
m_state += CPU_STATE_MAX;
m_state |= CPU_STATE_SLEEP;
}
void CPUThread::awake()
{
// must be called after the balanced sleep() call
if (m_state.atomic_op([](u64& state) -> bool
{
if (state < CPU_STATE_MAX)
{
throw EXCEPTION("sleep()/awake() inconsistency");
}
if ((state -= CPU_STATE_MAX) < CPU_STATE_MAX)
{
state &= ~CPU_STATE_SLEEP;
// notify the condition variable as well
return true;
}
return false;
}))
{
if (is_current()) return;
// lock for reliable notification; the condition being checked is probably externally set
std::lock_guard<std::mutex> lock(mutex);
cv.notify_one();
}
}
bool CPUThread::signal()
{
// try to set SIGNAL
if (m_state._or(CPU_STATE_SIGNAL) & CPU_STATE_SIGNAL)
{
return false;
}
else
{
// not truly responsible for signal delivery, requires additional measures like LV2_LOCK
cv.notify_one();
return true;
}
}
bool CPUThread::unsignal()
{
// remove SIGNAL and return its old value
return (m_state._and_not(CPU_STATE_SIGNAL) & CPU_STATE_SIGNAL) != 0;
}
bool CPUThread::check_status()
bool cpu_thread::check_status()
{
std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
@ -265,12 +64,12 @@ bool CPUThread::check_status()
{
CHECK_EMU_STATUS; // check at least once
if (!is_alive())
if (state & cpu_state::exit)
{
return true;
}
if (!is_paused() && (m_state & CPU_STATE_INTR) == 0)
if (!state.test(cpu_state_pause) && !state.test(cpu_state::interrupt))
{
break;
}
@ -281,7 +80,7 @@ bool CPUThread::check_status()
continue;
}
if (!is_paused() && (m_state & CPU_STATE_INTR) != 0 && handle_interrupt())
if (!state.test(cpu_state_pause) && state & cpu_state::interrupt && handle_interrupt())
{
continue;
}
@ -289,17 +88,45 @@ bool CPUThread::check_status()
cv.wait(lock);
}
if (m_state & CPU_STATE_RETURN || is_stopped())
const auto state_ = state.load();
if (state_ & to_mset(cpu_state::ret, cpu_state::stop))
{
return true;
}
if (m_state & CPU_STATE_STEP)
if (state_ & cpu_state::dbg_step)
{
// set PAUSE, but allow to execute once
m_state |= CPU_STATE_PAUSED;
m_state &= ~CPU_STATE_STEP;
state += cpu_state::dbg_pause;
state -= cpu_state::dbg_step;
}
return false;
}
std::vector<std::shared_ptr<cpu_thread>> get_all_cpu_threads()
{
std::vector<std::shared_ptr<cpu_thread>> result;
for (auto& t : idm::get_all<PPUThread>())
{
result.emplace_back(t);
}
for (auto& t : idm::get_all<SPUThread>())
{
result.emplace_back(t);
}
for (auto& t : idm::get_all<RawSPUThread>())
{
result.emplace_back(t);
}
for (auto& t : idm::get_all<ARMv7Thread>())
{
result.emplace_back(t);
}
return result;
}

View file

@ -2,174 +2,97 @@
#include "Utilities/Thread.h"
enum CPUThreadType
// CPU Thread Type
enum class cpu_type : u32
{
CPU_THREAD_PPU,
CPU_THREAD_SPU,
CPU_THREAD_RAW_SPU,
CPU_THREAD_ARMv7,
ppu, // PPU Thread
spu, // SPU Thread
arm, // ARMv7 Thread
};
// CPU Thread State Flags
enum : u64
// CPU Thread State flags
enum struct cpu_state : u32
{
CPU_STATE_STOPPED = (1ull << 0), // basic execution state (stopped by default), removed by Exec()
CPU_STATE_PAUSED = (1ull << 1), // pauses thread execution, set by the debugger (manually or after step execution)
CPU_STATE_SLEEP = (1ull << 2), // shouldn't affect thread execution, set by sleep(), removed by the latest awake(), may possibly indicate waiting state of the thread
CPU_STATE_STEP = (1ull << 3), // forces the thread to pause after executing just one instruction or something appropriate, set by the debugger
CPU_STATE_DEAD = (1ull << 4), // indicates irreversible exit of the thread
CPU_STATE_RETURN = (1ull << 5), // used for callback return
CPU_STATE_SIGNAL = (1ull << 6), // used for HLE signaling
CPU_STATE_INTR = (1ull << 7), // thread interrupted
stop, // Thread not running (HLE, initial state)
exit, // Irreversible exit
suspend, // Thread paused
ret, // Callback return requested
signal, // Thread received a signal (HLE)
interrupt, // Thread interrupted
CPU_STATE_MAX = (1ull << 8), // added to (subtracted from) m_state by sleep()/awake() calls to trigger status check
dbg_global_pause, // Emulation paused
dbg_global_stop, // Emulation stopped
dbg_pause, // Thread paused
dbg_step, // Thread forced to pause after one step (one instruction, etc)
};
class CPUThreadReturn {}; // "HLE return" exception event
class CPUThreadStop {}; // CPUThread::Stop exception event
class CPUThreadExit {}; // CPUThread::Exit exception event
// CPU Thread State flags: pause state union
constexpr mset<cpu_state> cpu_state_pause = to_mset(cpu_state::suspend, cpu_state::dbg_global_pause, cpu_state::dbg_pause);
class CPUDecoder;
class CPUThread : public named_thread_t
class cpu_thread : public named_thread
{
void on_task() override;
void on_id_aux_finalize() override { exit(); } // call exit() instead of join()
protected:
atomic_t<u64> m_state{ CPU_STATE_STOPPED }; // thread state flags
std::unique_ptr<CPUDecoder> m_dec;
const u32 m_id;
const CPUThreadType m_type;
const std::string m_name; // changing m_name is unsafe because it can be read at any moment
CPUThread(CPUThreadType type, const std::string& name);
public:
virtual ~CPUThread() override;
virtual void on_init() override
{
named_thread::on_init();
}
virtual std::string get_name() const override;
u32 get_id() const { return m_id; }
CPUThreadType get_type() const { return m_type; }
virtual void on_stop() override
{
state += cpu_state::exit;
safe_notify();
}
bool is_alive() const { return (m_state & CPU_STATE_DEAD) == 0; }
bool is_stopped() const { return (m_state & CPU_STATE_STOPPED) != 0; }
virtual bool is_paused() const;
const std::string name;
const u32 id{};
const cpu_type type;
virtual void dump_info() const;
virtual u32 get_pc() const = 0;
virtual u32 get_offset() const = 0;
virtual void do_run() = 0;
virtual void cpu_task() = 0;
cpu_thread(cpu_type type, const std::string& name)
: type(type)
, name(name)
{
}
virtual void init_regs() = 0;
virtual void init_stack() = 0;
virtual void close_stack() = 0;
// Public thread state
atomic_t<mset<cpu_state>> state{ cpu_state::stop };
// initialize thread
void run();
// Recursively enter sleep state
void sleep()
{
if (!++m_sleep) xsleep();
}
// called by the debugger, don't use
void pause();
// Leave sleep state
void awake()
{
if (!m_sleep--) xsleep();
}
// called by the debugger, don't use
void resume();
// stop thread execution
void stop();
// start thread execution (removing STOP status)
void exec();
// exit thread execution
void exit();
// called by the debugger, don't use
void step();
// trigger thread status check
void sleep();
// untrigger thread status check
void awake();
// set SIGNAL and notify (returns true if set)
bool signal();
// test SIGNAL and reset
bool unsignal();
// process m_state flags, returns true if the checker must return
// Process thread state, return true if the checker must return
bool check_status();
virtual std::string dump() const = 0; // Print CPU state
virtual void cpu_init() {}
virtual void cpu_task() = 0;
virtual bool handle_interrupt() { return false; }
std::string GetFName() const
private:
[[noreturn]] void xsleep()
{
return fmt::format("%s[0x%x] Thread (%s)", GetTypeString(), m_id, m_name);
throw std::runtime_error("cpu_thread: sleep()/awake() inconsistency");
}
static const char* CPUThreadTypeToString(CPUThreadType type)
{
switch (type)
{
case CPU_THREAD_PPU: return "PPU";
case CPU_THREAD_SPU: return "SPU";
case CPU_THREAD_RAW_SPU: return "RawSPU";
case CPU_THREAD_ARMv7: return "ARMv7";
}
return "Unknown";
}
const char* ThreadStatusToString() const
{
// TODO
//switch (ThreadStatus())
//{
//case CPUThread_Ready: return "Ready";
//case CPUThread_Running: return "Running";
//case CPUThread_Paused: return "Paused";
//case CPUThread_Stopped: return "Stopped";
//case CPUThread_Sleeping: return "Sleeping";
//case CPUThread_Break: return "Break";
//case CPUThread_Step: return "Step";
//}
return "Unknown";
}
const char* GetTypeString() const
{
return CPUThreadTypeToString(m_type);
}
CPUDecoder* GetDecoder()
{
return m_dec.get();
};
virtual std::string RegsToString() const = 0;
virtual std::string ReadRegString(const std::string& reg) const = 0;
virtual bool WriteRegString(const std::string& reg, std::string value) = 0;
// Sleep/Awake counter
atomic_t<u32> m_sleep{};
};
inline CPUThread* get_current_cpu_thread()
inline cpu_thread* get_current_cpu_thread() noexcept
{
extern thread_local CPUThread* g_tls_current_cpu_thread;
extern thread_local cpu_thread* g_tls_current_cpu_thread;
return g_tls_current_cpu_thread;
}
class cpu_thread
{
protected:
std::shared_ptr<CPUThread> thread;
public:
virtual cpu_thread& args(std::initializer_list<std::string> values) = 0;
virtual cpu_thread& run() = 0;
};
extern std::vector<std::shared_ptr<cpu_thread>> get_all_cpu_threads();

View file

@ -1,98 +0,0 @@
#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/Cell/SPUThread.h"
#include "Emu/Cell/RawSPUThread.h"
#include "Emu/ARMv7/ARMv7Thread.h"
#include "CPUThreadManager.h"
CPUThreadManager::CPUThreadManager()
{
}
CPUThreadManager::~CPUThreadManager()
{
}
void CPUThreadManager::Close()
{
std::lock_guard<std::mutex> lock(m_mutex);
for (auto& x : m_raw_spu)
{
x.reset();
}
}
std::vector<std::shared_ptr<CPUThread>> CPUThreadManager::GetAllThreads()
{
std::vector<std::shared_ptr<CPUThread>> result;
for (auto& t : idm::get_all<PPUThread>())
{
result.emplace_back(t);
}
for (auto& t : idm::get_all<SPUThread>())
{
result.emplace_back(t);
}
for (auto& t : idm::get_all<RawSPUThread>())
{
result.emplace_back(t);
}
for (auto& t : idm::get_all<ARMv7Thread>())
{
result.emplace_back(t);
}
return result;
}
void CPUThreadManager::Exec()
{
for (auto& t : idm::get_all<PPUThread>())
{
t->exec();
}
for (auto& t : idm::get_all<ARMv7Thread>())
{
t->exec();
}
}
std::shared_ptr<RawSPUThread> CPUThreadManager::NewRawSPUThread()
{
std::lock_guard<std::mutex> lock(m_mutex);
std::shared_ptr<RawSPUThread> result;
for (u32 i = 0; i < m_raw_spu.size(); i++)
{
if (m_raw_spu[i].expired())
{
m_raw_spu[i] = result = idm::make_ptr<RawSPUThread>(std::to_string(i), i);
break;
}
}
return result;
}
std::shared_ptr<RawSPUThread> CPUThreadManager::GetRawSPUThread(u32 index)
{
if (index >= m_raw_spu.size())
{
return nullptr;
}
std::lock_guard<std::mutex> lock(m_mutex);
return m_raw_spu[index].lock();
}

View file

@ -1,25 +0,0 @@
#pragma once
class CPUThread;
class RawSPUThread;
class CPUThreadManager final
{
std::mutex m_mutex;
std::array<std::weak_ptr<RawSPUThread>, 5> m_raw_spu;
public:
CPUThreadManager();
~CPUThreadManager();
void Close();
static std::vector<std::shared_ptr<CPUThread>> GetAllThreads();
static void Exec();
std::shared_ptr<RawSPUThread> NewRawSPUThread();
std::shared_ptr<RawSPUThread> GetRawSPUThread(u32 index);
};

View file

@ -1,54 +0,0 @@
#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/SysCalls/lv2/sys_event.h"
#include "Event.h"
void EventManager::Init()
{
}
void EventManager::Clear()
{
m_map.clear();
}
bool EventManager::UnregisterKey(u64 key)
{
if (!key)
{
// always ok
return true;
}
std::lock_guard<std::mutex> lock(m_mutex);
auto f = m_map.find(key);
if (f != m_map.end())
{
m_map.erase(f);
return true;
}
return false;
}
std::shared_ptr<lv2_event_queue_t> EventManager::GetEventQueue(u64 key)
{
if (!key)
{
// never exists
return nullptr;
}
std::lock_guard<std::mutex> lock(m_mutex);
auto f = m_map.find(key);
if (f != m_map.end())
{
return f->second;
}
return nullptr;
}

View file

@ -1,37 +0,0 @@
#pragma once
#include "Emu/IdManager.h"
struct lv2_event_queue_t;
class EventManager
{
std::mutex m_mutex;
std::unordered_map<u64, std::shared_ptr<lv2_event_queue_t>> m_map;
public:
void Init();
void Clear();
bool UnregisterKey(u64 key);
template<typename... Args, typename = std::enable_if_t<std::is_constructible<lv2_event_queue_t, Args...>::value>> std::shared_ptr<lv2_event_queue_t> MakeEventQueue(u64 key, Args&&... args)
{
std::lock_guard<std::mutex> lock(m_mutex);
if (key && m_map.find(key) != m_map.end())
{
return nullptr;
}
auto queue = idm::make_ptr<lv2_event_queue_t>(std::forward<Args>(args)...);
if (key)
{
m_map[key] = queue;
}
return queue;
}
std::shared_ptr<lv2_event_queue_t> GetEventQueue(u64 key);
};

View file

@ -1,820 +0,0 @@
#include "stdafx.h"
#include "HDD.h"
void vfsHDDManager::CreateBlock(vfsHDD_Block& block)
{
block.is_used = true;
block.next_block = 0;
}
void vfsHDDManager::CreateEntry(vfsHDD_Entry& entry)
{
memset(&entry, 0, sizeof(vfsHDD_Entry));
u64 ctime = time(nullptr);
entry.atime = ctime;
entry.ctime = ctime;
entry.mtime = ctime;
entry.access = 0666;
CreateBlock(entry);
}
void vfsHDDManager::CreateHDD(const std::string& path, u64 size, u64 block_size)
{
fs::file f(path, fom::rewrite);
static const u64 cur_dir_block = 1;
vfsHDD_Hdr hdr;
CreateBlock(hdr);
hdr.next_block = cur_dir_block;
hdr.magic = g_hdd_magic;
hdr.version = g_hdd_version;
hdr.block_count = (size + block_size) / block_size;
hdr.block_size = block_size;
f.write(&hdr, sizeof(vfsHDD_Hdr));
{
vfsHDD_Entry entry;
CreateEntry(entry);
entry.type = vfsHDD_Entry_Dir;
entry.data_block = hdr.next_block;
entry.next_block = 0;
f.seek(cur_dir_block * hdr.block_size);
f.write(&entry, sizeof(vfsHDD_Entry));
f.write(".", 1);
}
u8 null = 0;
CHECK_ASSERTION(f.seek(hdr.block_count * hdr.block_size - sizeof(null)) != -1);
f.write(&null, sizeof(null));
}
void vfsHDDManager::Format()
{
}
void vfsHDDManager::AppendEntry(vfsHDD_Entry entry)
{
}
bool vfsHDDFile::goto_block(u64 n)
{
vfsHDD_Block block_info;
if (m_info.data_block >= m_hdd_info.block_count)
{
return false;
}
CHECK_ASSERTION(m_hdd.Seek(m_info.data_block * m_hdd_info.block_size) != -1);
block_info.next_block = m_info.data_block;
for (u64 i = 0; i < n; ++i)
{
if (!block_info.next_block || !block_info.is_used || block_info.next_block >= m_hdd_info.block_count)
{
return false;
}
CHECK_ASSERTION(m_hdd.Seek(block_info.next_block * m_hdd_info.block_size) != -1);
m_hdd.Read(&block_info, sizeof(vfsHDD_Block));
}
return true;
}
void vfsHDDFile::RemoveBlocks(u64 start_block)
{
vfsHDD_Block block_info;
block_info.next_block = start_block;
while (block_info.next_block && block_info.is_used)
{
u64 offset = block_info.next_block * m_hdd_info.block_size;
ReadBlock(offset, block_info);
WriteBlock(offset, g_null_block);
}
}
void vfsHDDFile::WriteBlock(u64 block, const vfsHDD_Block& data)
{
CHECK_ASSERTION(m_hdd.Seek(block * m_hdd_info.block_size) != -1);
m_hdd.Write(&data, sizeof(vfsHDD_Block));
}
void vfsHDDFile::ReadBlock(u64 block, vfsHDD_Block& data)
{
CHECK_ASSERTION(m_hdd.Seek(block * m_hdd_info.block_size) != -1);
m_hdd.Read(&data, sizeof(vfsHDD_Block));
}
void vfsHDDFile::WriteEntry(u64 block, const vfsHDD_Entry& data)
{
CHECK_ASSERTION(m_hdd.Seek(block * m_hdd_info.block_size) != -1);
m_hdd.Write(&data, sizeof(vfsHDD_Entry));
}
void vfsHDDFile::ReadEntry(u64 block, vfsHDD_Entry& data)
{
CHECK_ASSERTION(m_hdd.Seek(block * m_hdd_info.block_size) != -1);
m_hdd.Read(&data, sizeof(vfsHDD_Entry));
}
void vfsHDDFile::ReadEntry(u64 block, vfsHDD_Entry& data, std::string& name)
{
CHECK_ASSERTION(m_hdd.Seek(block * m_hdd_info.block_size) != -1);
m_hdd.Read(&data, sizeof(vfsHDD_Entry));
name.resize(GetMaxNameLen());
m_hdd.Read(&name.front(), GetMaxNameLen());
}
void vfsHDDFile::ReadEntry(u64 block, std::string& name)
{
CHECK_ASSERTION(m_hdd.Seek(block * m_hdd_info.block_size + sizeof(vfsHDD_Entry)) != -1);
name.resize(GetMaxNameLen());
m_hdd.Read(&name.front(), GetMaxNameLen());
}
void vfsHDDFile::WriteEntry(u64 block, const vfsHDD_Entry& data, const std::string& name)
{
CHECK_ASSERTION(m_hdd.Seek(block * m_hdd_info.block_size) != -1);
m_hdd.Write(&data, sizeof(vfsHDD_Entry));
m_hdd.Write(name.c_str(), std::min<size_t>(GetMaxNameLen() - 1, name.length() + 1));
}
void vfsHDDFile::Open(u64 info_block)
{
m_info_block = info_block;
ReadEntry(m_info_block, m_info);
m_position = 0;
m_cur_block = m_info.data_block;
}
u64 vfsHDDFile::FindFreeBlock()
{
vfsHDD_Block block_info;
for (u64 i = 0; i < m_hdd_info.block_count; ++i)
{
ReadBlock(i, block_info);
if (!block_info.is_used)
{
return i;
}
}
return 0;
}
bool vfsHDDFile::Seek(u64 pos)
{
if (!goto_block(pos / m_hdd_info.block_size))
{
return false;
}
m_position = pos % m_hdd_info.block_size;
return true;
}
void vfsHDDFile::SaveInfo()
{
CHECK_ASSERTION(m_hdd.Seek(m_info_block * m_hdd_info.block_size) != -1);
CHECK_ASSERTION(m_hdd.Seek(m_info_block * m_hdd_info.block_size) != -1);
m_hdd.Write(&m_info, sizeof(vfsHDD_Entry));
}
u64 vfsHDDFile::Read(void* dst, u64 size)
{
if (!size)
return 0;
//vfsDeviceLocker lock(m_hdd);
const u32 block_size = m_hdd_info.block_size - sizeof(vfsHDD_Block);
u64 rsize = std::min<u64>(block_size - m_position, size);
vfsHDD_Block cur_block_info;
CHECK_ASSERTION(m_hdd.Seek(m_cur_block * m_hdd_info.block_size) != -1);
m_hdd.Read(&cur_block_info, sizeof(vfsHDD_Block));
CHECK_ASSERTION(m_hdd.Seek(m_cur_block * m_hdd_info.block_size + sizeof(vfsHDD_Block) + m_position) != -1);
m_hdd.Read(dst, rsize);
size -= rsize;
m_position += rsize;
if (!size)
{
return rsize;
}
u64 offset = rsize;
for (; size; size -= rsize, offset += rsize)
{
if (!cur_block_info.is_used || !cur_block_info.next_block || cur_block_info.next_block >= m_hdd_info.block_count)
{
return offset;
}
m_cur_block = cur_block_info.next_block;
rsize = std::min<u64>(block_size, size);
CHECK_ASSERTION(m_hdd.Seek(cur_block_info.next_block * m_hdd_info.block_size) != -1);
m_hdd.Read(&cur_block_info, sizeof(vfsHDD_Block));
if (m_hdd.Read((u8*)dst + offset, rsize) != rsize)
{
return offset;
}
}
m_position = rsize;
return offset;
}
u64 vfsHDDFile::Write(const void* src, u64 size)
{
if (!size)
return 0;
//vfsDeviceLocker lock(m_hdd);
const u32 block_size = m_hdd_info.block_size - sizeof(vfsHDD_Block);
if (!m_cur_block)
{
if (!m_info.data_block)
{
u64 new_block = FindFreeBlock();
if (!new_block)
{
return 0;
}
WriteBlock(new_block, g_used_block);
m_info.data_block = new_block;
m_info.size = 0;
SaveInfo();
}
m_cur_block = m_info.data_block;
m_position = 0;
}
u64 wsize = std::min<u64>(block_size - m_position, size);
vfsHDD_Block block_info;
ReadBlock(m_cur_block, block_info);
if (wsize)
{
CHECK_ASSERTION(m_hdd.Seek(m_cur_block * m_hdd_info.block_size + sizeof(vfsHDD_Block) + m_position) != -1);
m_hdd.Write(src, wsize);
size -= wsize;
m_info.size += wsize;
m_position += wsize;
SaveInfo();
if (!size)
return wsize;
}
u64 last_block = m_cur_block;
block_info.is_used = true;
u64 offset = wsize;
for (; size; size -= wsize, offset += wsize, m_info.size += wsize)
{
u64 new_block = FindFreeBlock();
if (!new_block)
{
m_position = 0;
SaveInfo();
return offset;
}
m_cur_block = new_block;
wsize = std::min<u64>(block_size, size);
block_info.next_block = m_cur_block;
CHECK_ASSERTION(m_hdd.Seek(last_block * m_hdd_info.block_size) != -1);
if (m_hdd.Write(&block_info, sizeof(vfsHDD_Block)) != sizeof(vfsHDD_Block))
{
m_position = 0;
SaveInfo();
return offset;
}
block_info.next_block = 0;
CHECK_ASSERTION(m_hdd.Seek(m_cur_block * m_hdd_info.block_size) != -1);
if (m_hdd.Write(&block_info, sizeof(vfsHDD_Block)) != sizeof(vfsHDD_Block))
{
m_position = 0;
SaveInfo();
return offset;
}
if ((m_position = m_hdd.Write((u8*)src + offset, wsize)) != wsize)
{
m_info.size += wsize;
SaveInfo();
return offset;
}
last_block = m_cur_block;
}
SaveInfo();
m_position = wsize;
return offset;
}
vfsDeviceHDD::vfsDeviceHDD(const std::string& hdd_path) : m_hdd_path(hdd_path)
{
}
vfsFileBase* vfsDeviceHDD::GetNewFileStream()
{
return new vfsHDD(this, m_hdd_path);
}
vfsDirBase* vfsDeviceHDD::GetNewDirStream()
{
return nullptr;
}
vfsHDD::vfsHDD(vfsDevice* device, const std::string& hdd_path)
: m_hdd_file(device)
, m_file(m_hdd_file, m_hdd_info)
, m_hdd_path(hdd_path)
, vfsFileBase(device)
{
m_hdd_file.Open(hdd_path, fom::read | fom::write);
m_hdd_file.Read(&m_hdd_info, sizeof(vfsHDD_Hdr));
m_cur_dir_block = m_hdd_info.next_block;
if (!m_hdd_info.block_size)
{
LOG_ERROR(HLE, "Bad block size!");
m_hdd_info.block_size = 2048;
}
CHECK_ASSERTION(m_hdd_file.Seek(m_cur_dir_block * m_hdd_info.block_size) != -1);
m_hdd_file.Read(&m_cur_dir, sizeof(vfsHDD_Entry));
}
bool vfsHDD::SearchEntry(const std::string& name, u64& entry_block, u64* parent_block)
{
u64 last_block = 0;
u64 block = m_cur_dir_block;
vfsHDD_Entry entry;
std::string buf;
while (block)
{
ReadEntry(block, entry, buf);
if (fmt::CmpNoCase(name, buf) == 0)
{
entry_block = block;
if (parent_block)
*parent_block = last_block;
return true;
}
last_block = block;
block = entry.is_used ? entry.next_block : 0ULL;
}
return false;
}
s32 vfsHDD::OpenDir(const std::string& name)
{
LOG_WARNING(HLE, "OpenDir(%s)", name.c_str());
u64 entry_block;
if (!SearchEntry(name, entry_block))
{
return -1;
}
CHECK_ASSERTION(m_hdd_file.Seek(entry_block * m_hdd_info.block_size) != -1);
vfsHDD_Entry entry;
m_hdd_file.Read(&entry, sizeof(vfsHDD_Entry));
if (entry.type == vfsHDD_Entry_File)
{
return 1;
}
m_cur_dir_block = entry.data_block;
ReadEntry(m_cur_dir_block, m_cur_dir);
return 0;
}
bool vfsHDD::Rename(const std::string& from, const std::string& to)
{
u64 entry_block;
if (!SearchEntry(from, entry_block))
{
return false;
}
vfsHDD_Entry entry;
ReadEntry(entry_block, entry);
WriteEntry(entry_block, entry, to);
return true;
}
u64 vfsHDD::FindFreeBlock()
{
vfsHDD_Block block_info;
for (u64 i = 0; i < m_hdd_info.block_count; ++i)
{
ReadBlock(i, block_info);
if (!block_info.is_used)
{
return i;
}
}
return 0;
}
void vfsHDD::WriteBlock(u64 block, const vfsHDD_Block& data)
{
CHECK_ASSERTION(m_hdd_file.Seek(block * m_hdd_info.block_size) != -1);
m_hdd_file.Write(&data, sizeof(vfsHDD_Block));
}
void vfsHDD::ReadBlock(u64 block, vfsHDD_Block& data)
{
CHECK_ASSERTION(m_hdd_file.Seek(block * m_hdd_info.block_size) != -1);
m_hdd_file.Read(&data, sizeof(vfsHDD_Block));
}
void vfsHDD::WriteEntry(u64 block, const vfsHDD_Entry& data)
{
CHECK_ASSERTION(m_hdd_file.Seek(block * m_hdd_info.block_size) != -1);
m_hdd_file.Write(&data, sizeof(vfsHDD_Entry));
}
void vfsHDD::ReadEntry(u64 block, vfsHDD_Entry& data)
{
CHECK_ASSERTION(m_hdd_file.Seek(block * m_hdd_info.block_size) != -1);
m_hdd_file.Read(&data, sizeof(vfsHDD_Entry));
}
void vfsHDD::ReadEntry(u64 block, vfsHDD_Entry& data, std::string& name)
{
CHECK_ASSERTION(m_hdd_file.Seek(block * m_hdd_info.block_size) != -1);
m_hdd_file.Read(&data, sizeof(vfsHDD_Entry));
name.resize(GetMaxNameLen());
m_hdd_file.Read(&name.front(), GetMaxNameLen());
}
void vfsHDD::ReadEntry(u64 block, std::string& name)
{
CHECK_ASSERTION(m_hdd_file.Seek(block * m_hdd_info.block_size + sizeof(vfsHDD_Entry)) != -1);
name.resize(GetMaxNameLen());
m_hdd_file.Read(&name.front(), GetMaxNameLen());
}
void vfsHDD::WriteEntry(u64 block, const vfsHDD_Entry& data, const std::string& name)
{
CHECK_ASSERTION(m_hdd_file.Seek(block * m_hdd_info.block_size) != -1);
m_hdd_file.Write(&data, sizeof(vfsHDD_Entry));
m_hdd_file.Write(name.c_str(), std::min<size_t>(GetMaxNameLen() - 1, name.length() + 1));
}
bool vfsHDD::Create(vfsHDD_EntryType type, const std::string& name)
{
if (HasEntry(name))
{
return false;
}
u64 new_block = FindFreeBlock();
if (!new_block)
{
return false;
}
LOG_NOTICE(HLE, "CREATING ENTRY AT 0x%llx", new_block);
WriteBlock(new_block, g_used_block);
{
vfsHDD_Entry new_entry;
vfsHDDManager::CreateEntry(new_entry);
new_entry.next_block = 0;
new_entry.type = type;
if (type == vfsHDD_Entry_Dir)
{
u64 block_cur = FindFreeBlock();
if (!block_cur)
{
return false;
}
WriteBlock(block_cur, g_used_block);
u64 block_last = FindFreeBlock();
if (!block_last)
{
return false;
}
WriteBlock(block_last, g_used_block);
vfsHDD_Entry entry_cur, entry_last;
vfsHDDManager::CreateEntry(entry_cur);
vfsHDDManager::CreateEntry(entry_last);
entry_cur.type = vfsHDD_Entry_Dir;
entry_cur.data_block = block_cur;
entry_cur.next_block = block_last;
entry_last.type = vfsHDD_Entry_Dir;
entry_last.data_block = m_cur_dir_block;
entry_last.next_block = 0;
new_entry.data_block = block_cur;
WriteEntry(block_cur, entry_cur, ".");
WriteEntry(block_last, entry_last, "..");
}
WriteEntry(new_block, new_entry, name);
}
{
u64 block = m_cur_dir_block;
vfsHDD_Block tmp;
while (block)
{
ReadBlock(block, tmp);
if (!tmp.next_block)
break;
block = tmp.next_block;
}
tmp.next_block = new_block;
WriteBlock(block, tmp);
}
return true;
}
bool vfsHDD::GetFirstEntry(u64& block, vfsHDD_Entry& entry, std::string& name)
{
if (!m_cur_dir_block)
{
return false;
}
ReadEntry(m_cur_dir_block, entry, name);
block = entry.is_used ? entry.next_block : 0;
return true;
}
bool vfsHDD::GetNextEntry(u64& block, vfsHDD_Entry& entry, std::string& name)
{
if (!block)
{
return false;
}
ReadEntry(block, entry, name);
block = entry.is_used ? entry.next_block : 0;
return true;
}
bool vfsHDD::Open(const std::string& path, u32 mode)
{
const char* s = path.c_str();
u64 from = 0;
u64 pos = 0;
u64 file_pos = -1;
do
{
if (s[pos] == '\\' || s[pos] == '/' || s[pos] == '\0') // ???
{
if (file_pos != -1)
{
return false;
}
if (from != -1)
{
if (pos - from > 1)
{
s32 res = OpenDir(std::string(s + from, pos));
if (res == -1)
{
return false;
}
else if (res == 1)
{
file_pos = from;
}
}
from = pos;
}
else
{
from = pos;
}
}
} while (s[pos++] != '\0');
if (file_pos == -1)
{
return false;
}
u64 file_block;
if (!SearchEntry(std::string(s + file_pos), file_block))
{
return false;
}
LOG_NOTICE(HLE, "ENTRY FOUND AT 0x%llx", file_block);
m_file.Open(file_block);
return vfsFileBase::Open(path, mode);
}
bool vfsHDD::HasEntry(const std::string& name)
{
u64 file_block;
if (!SearchEntry(name, file_block))
{
return false;
}
return true;
}
void vfsHDD::RemoveBlocksDir(u64 start_block)
{
std::string name;
u64 block = start_block;
vfsHDD_Entry entry;
while (block)
{
ReadEntry(block, entry, name);
WriteBlock(block, g_null_block);
if (entry.type == vfsHDD_Entry_Dir && name != "." && name != "..")
{
LOG_WARNING(HLE, "Removing sub folder '%s'", name.c_str());
RemoveBlocksDir(entry.data_block);
}
else if (entry.type == vfsHDD_Entry_File)
{
RemoveBlocksFile(entry.data_block);
}
block = entry.next_block;
}
}
void vfsHDD::RemoveBlocksFile(u64 start_block)
{
u64 block = start_block;
vfsHDD_Block block_data;
while (block)
{
ReadBlock(block, block_data);
WriteBlock(block, g_null_block);
block = block_data.next_block;
}
}
bool vfsHDD::RemoveEntry(const std::string& name)
{
u64 entry_block, parent_entry;
if (!SearchEntry(name, entry_block, &parent_entry))
{
return false;
}
vfsHDD_Entry entry;
ReadEntry(entry_block, entry);
if (entry.type == vfsHDD_Entry_Dir)
{
RemoveBlocksDir(entry.data_block);
}
else if (entry.type == vfsHDD_Entry_File)
{
RemoveBlocksFile(entry.data_block);
}
if (parent_entry)
{
u64 next = entry.next_block;
ReadEntry(parent_entry, entry);
entry.next_block = next;
WriteEntry(parent_entry, entry);
}
WriteBlock(entry_block, g_null_block);
return true;
}
u64 vfsHDD::Write(const void* src, u64 size)
{
return m_file.Write(src, size); // ???
}
u64 vfsHDD::Read(void* dst, u64 size)
{
return m_file.Read(dst, size); // ???
}
u64 vfsHDD::Seek(s64 offset, fs::seek_mode whence)
{
switch (whence)
{
case fs::seek_set: return m_file.Seek(offset);
case fs::seek_cur: return m_file.Seek(Tell() + offset);
case fs::seek_end: return m_file.Seek(m_file.GetSize() + offset);
}
throw EXCEPTION("Unknown whence (0x%x)", whence);
}
u64 vfsHDD::Tell() const
{
return m_file.Tell(); // ???
}
bool vfsHDD::Eof() const
{
return m_file.Eof();
}
bool vfsHDD::IsOpened() const
{
return true; // ???
}
u64 vfsHDD::GetSize() const
{
return m_file.GetSize();
}

View file

@ -1,208 +0,0 @@
#pragma once
#include "Emu/FS/vfsDevice.h"
#include "Emu/FS/vfsLocalFile.h"
static const u64 g_hdd_magic = *(u64*)"PS3eHDD\0";
static const u16 g_hdd_version = 0x0001;
struct vfsHDD_Block
{
struct
{
u64 is_used : 1;
u64 next_block : 63;
};
} static const g_null_block = {0}, g_used_block = {1};
struct vfsHDD_Hdr : public vfsHDD_Block
{
u64 magic;
u16 version;
u64 block_count;
u32 block_size;
};
enum vfsHDD_EntryType : u8
{
vfsHDD_Entry_Dir = 0,
vfsHDD_Entry_File = 1,
vfsHDD_Entry_Link = 2,
};
struct vfsHDD_Entry : public vfsHDD_Block
{
u64 data_block;
u32 access;
vfsHDD_EntryType type;
u64 size;
u64 ctime;
u64 mtime;
u64 atime;
};
class vfsHDDManager
{
public:
static void CreateBlock(vfsHDD_Block& block);
static void CreateEntry(vfsHDD_Entry& entry);
static void CreateHDD(const std::string& path, u64 size, u64 block_size);
void Format();
void AppendEntry(vfsHDD_Entry entry);
};
class vfsHDDFile
{
u64 m_info_block;
vfsHDD_Entry m_info;
const vfsHDD_Hdr& m_hdd_info;
vfsLocalFile& m_hdd;
u32 m_position;
u64 m_cur_block;
bool goto_block(u64 n);
void RemoveBlocks(u64 start_block);
void WriteBlock(u64 block, const vfsHDD_Block& data);
void ReadBlock(u64 block, vfsHDD_Block& data);
void WriteEntry(u64 block, const vfsHDD_Entry& data);
void ReadEntry(u64 block, vfsHDD_Entry& data);
void ReadEntry(u64 block, vfsHDD_Entry& data, std::string& name);
void ReadEntry(u64 block, std::string& name);
void WriteEntry(u64 block, const vfsHDD_Entry& data, const std::string& name);
force_inline u32 GetMaxNameLen() const
{
return m_hdd_info.block_size - sizeof(vfsHDD_Entry);
}
public:
vfsHDDFile(vfsLocalFile& hdd, const vfsHDD_Hdr& hdd_info)
: m_hdd(hdd)
, m_hdd_info(hdd_info)
{
}
~vfsHDDFile()
{
}
void Open(u64 info_block);
u64 FindFreeBlock();
u64 GetSize() const
{
return m_info.size;
}
bool Seek(u64 pos);
u64 Tell() const
{
return m_cur_block * m_hdd_info.block_size + m_position; // ???
}
void SaveInfo();
u64 Read(void* dst, u64 size);
u64 Write(const void* src, u64 size);
bool Eof() const
{
return m_info.size <= (m_cur_block * m_hdd_info.block_size + m_position);
}
};
class vfsDeviceHDD : public vfsDevice
{
std::string m_hdd_path;
public:
vfsDeviceHDD(const std::string& hdd_path);
virtual vfsFileBase* GetNewFileStream() override;
virtual vfsDirBase* GetNewDirStream() override;
};
class vfsHDD : public vfsFileBase
{
vfsHDD_Hdr m_hdd_info;
vfsLocalFile m_hdd_file;
const std::string& m_hdd_path;
vfsHDD_Entry m_cur_dir;
u64 m_cur_dir_block;
vfsHDDFile m_file;
public:
vfsHDD(vfsDevice* device, const std::string& hdd_path);
force_inline u32 GetMaxNameLen() const
{
return m_hdd_info.block_size - sizeof(vfsHDD_Entry);
}
bool SearchEntry(const std::string& name, u64& entry_block, u64* parent_block = nullptr);
s32 OpenDir(const std::string& name);
bool Rename(const std::string& from, const std::string& to);
u64 FindFreeBlock();
void WriteBlock(u64 block, const vfsHDD_Block& data);
void ReadBlock(u64 block, vfsHDD_Block& data);
void WriteEntry(u64 block, const vfsHDD_Entry& data);
void ReadEntry(u64 block, vfsHDD_Entry& data);
void ReadEntry(u64 block, vfsHDD_Entry& data, std::string& name);
void ReadEntry(u64 block, std::string& name);
void WriteEntry(u64 block, const vfsHDD_Entry& data, const std::string& name);
bool Create(vfsHDD_EntryType type, const std::string& name);
bool GetFirstEntry(u64& block, vfsHDD_Entry& entry, std::string& name);
bool GetNextEntry(u64& block, vfsHDD_Entry& entry, std::string& name);
virtual bool Open(const std::string& path, u32 mode = fom::read) override;
bool HasEntry(const std::string& name);
void RemoveBlocksDir(u64 start_block);
void RemoveBlocksFile(u64 start_block);
bool RemoveEntry(const std::string& name);
virtual u64 Write(const void* src, u64 count) override;
virtual u64 Read(void* dst, u64 count) override;
virtual u64 Seek(s64 offset, fs::seek_mode whence = fs::seek_set) override;
virtual u64 Tell() const override;
virtual bool Eof() const override;
virtual bool IsOpened() const override;
virtual u64 GetSize() const override;
};

View file

@ -1,156 +0,0 @@
#include "stdafx.h"
#include "IdManager.h"
namespace idm
{
shared_mutex g_mutex;
idm::map_t g_map;
u32 g_last_raw_id = 0;
thread_local u32 g_tls_last_id = 0xdeadbeef;
}
namespace fxm
{
shared_mutex g_mutex;
fxm::map_t g_map;
}
void idm::clear()
{
std::lock_guard<shared_mutex> lock(g_mutex);
// Call recorded finalization functions for all IDs
for (auto& id : idm::map_t(std::move(g_map)))
{
(*id.second.type_index)(id.second.data.get());
}
g_last_raw_id = 0;
}
bool idm::check(u32 in_id, id_type_index_t type)
{
reader_lock lock(g_mutex);
const auto found = g_map.find(in_id);
return found != g_map.end() && found->second.type_index == type;
}
const std::type_info* idm::get_type(u32 raw_id)
{
reader_lock lock(g_mutex);
const auto found = g_map.find(raw_id);
return found == g_map.end() ? nullptr : found->second.info;
}
std::shared_ptr<void> idm::get(u32 in_id, id_type_index_t type)
{
reader_lock lock(g_mutex);
const auto found = g_map.find(in_id);
if (found == g_map.end() || found->second.type_index != type)
{
return nullptr;
}
return found->second.data;
}
idm::map_t idm::get_all(id_type_index_t type)
{
reader_lock lock(g_mutex);
idm::map_t result;
for (auto& id : g_map)
{
if (id.second.type_index == type)
{
result.insert(id);
}
}
return result;
}
std::shared_ptr<void> idm::withdraw(u32 in_id, id_type_index_t type)
{
std::lock_guard<shared_mutex> lock(g_mutex);
const auto found = g_map.find(in_id);
if (found == g_map.end() || found->second.type_index != type)
{
return nullptr;
}
auto ptr = std::move(found->second.data);
g_map.erase(found);
return ptr;
}
u32 idm::get_count(id_type_index_t type)
{
reader_lock lock(g_mutex);
u32 result = 0;
for (auto& id : g_map)
{
if (id.second.type_index == type)
{
result++;
}
}
return result;
}
void fxm::clear()
{
std::lock_guard<shared_mutex> lock(g_mutex);
// Call recorded finalization functions for all IDs
for (auto& id : fxm::map_t(std::move(g_map)))
{
if (id.second) (*id.first)(id.second.get());
}
}
bool fxm::check(id_type_index_t type)
{
reader_lock lock(g_mutex);
const auto found = g_map.find(type);
return found != g_map.end() && found->second;
}
std::shared_ptr<void> fxm::get(id_type_index_t type)
{
reader_lock lock(g_mutex);
const auto found = g_map.find(type);
return found != g_map.end() ? found->second : nullptr;
}
std::shared_ptr<void> fxm::withdraw(id_type_index_t type)
{
std::unique_lock<shared_mutex> lock(g_mutex);
const auto found = g_map.find(type);
return found != g_map.end() ? std::move(found->second) : nullptr;
}

View file

@ -2,86 +2,154 @@
#include "Utilities/SharedMutex.h"
#define ID_MANAGER_INCLUDED
#include <set>
#include <map>
// TODO: make id_aux_initialize and id_aux_finalize safer against a possible ODR violation
// Function called after the successfull creation of an ID (does nothing by default, provide an overload)
inline void id_aux_initialize(void*)
// Mostly helper namespace
namespace id_manager
{
;
}
// Function called after the ID removal (does nothing by default, provide an overload)
inline void id_aux_finalize(void*)
{
;
}
// Type-erased id_aux_* function type
using id_aux_func_t = void(*)(void*);
template<typename T>
struct id_type_info_t
{
static const auto size = sizeof(T); // forbid forward declarations
static const id_aux_func_t on_remove;
};
// Type-erased finalization function
template<typename T>
const id_aux_func_t id_type_info_t<T>::on_remove = [](void* ptr)
{
return id_aux_finalize(static_cast<T*>(ptr));
};
using id_type_index_t = const id_aux_func_t*;
// Get a unique pointer to the on_remove value (will be unique for each type)
template<typename T>
inline constexpr id_type_index_t get_id_type_index()
{
return &id_type_info_t<T>::on_remove;
}
// Default ID traits for any arbitrary type
template<typename T>
struct id_traits
{
static const auto size = sizeof(T); // forbid forward declarations
// Get next mapped id (may return 0 if out of IDs)
static u32 next_id(u32 raw_id) { return raw_id < 0x80000000 ? (raw_id + 1) & 0x7fffffff : 0; }
// Convert "public" id to mapped id (may return 0 if invalid)
static u32 in_id(u32 id) { return id; }
// Convert mapped id to "public" id
static u32 out_id(u32 raw_id) { return raw_id; }
};
// ID Manager
// 0 is invalid ID
// 1..0x7fffffff : general purpose IDs
// 0x80000000+ : reserved (may be used through id_traits specializations)
namespace idm
{
struct id_data_t final
// Optional ID traits
template<typename T, typename = void>
struct id_traits
{
std::shared_ptr<void> data;
const std::type_info* info;
id_type_index_t type_index;
using tag = void;
template<typename T> id_data_t(const std::shared_ptr<T>& data)
: data(data)
, info(&typeid(T))
, type_index(get_id_type_index<T>())
static constexpr u32 min = 1;
static constexpr u32 max = 0x7fffffff;
};
template<typename T>
struct id_traits<T, void_t<typename T::id_base, decltype(&T::id_min), decltype(&T::id_max)>>
{
using tag = typename T::id_base;
static constexpr u32 min = T::id_min;
static constexpr u32 max = T::id_max;
};
// Optional ID storage
template<typename T, typename = void>
struct id_storage
{
static const u32* get(T*)
{
return nullptr;
}
};
template<typename T>
struct id_storage<T, void_t<decltype(&T::id)>>
{
static const u32* get(T* ptr)
{
return &ptr->id;
}
};
// Optional object initialization function (called after ID registration)
template<typename T, typename = void>
struct on_init
{
static void func(T*)
{
}
};
// Custom hasher for ID values (map to itself)
template<typename T>
struct on_init<T, decltype(std::declval<T>().on_init())>
{
static void func(T* ptr)
{
ptr->on_init();
}
};
// Optional object finalization function (called after ID removal)
template<typename T, typename = void>
struct on_stop
{
static void func(T*)
{
}
};
template<typename T>
struct on_stop<T, decltype(std::declval<T>().on_stop())>
{
static void func(T* ptr)
{
ptr->on_stop();
}
};
template<typename>
class typeinfo
{
// Global variable for each registered type
template<typename T>
struct registered
{
static const u32 index;
};
// Access global type list
static never_inline auto& access()
{
static std::vector<typeinfo> list;
return list;
}
static never_inline u32 add_type(typeinfo info)
{
auto& list = access();
list.emplace_back(info);
return ::size32(list) - 1;
}
public:
const std::type_info* info;
void(*on_init)(void*);
void(*on_stop)(void*);
// Get type index
template<typename T>
static inline u32 get_index()
{
// Forbid forward declarations (It'd be better to allow them sometimes but it seems too dangerous)
static constexpr auto size = sizeof(std::conditional_t<std::is_void<T>::value, void*, T>);
return registered<T>::index;
}
// Read all registered types
static inline const auto& get()
{
return access();
}
};
template<typename TAG> template<typename T>
const u32 typeinfo<TAG>::registered<T>::index = typeinfo<TAG>::add_type(
{
&typeid(T),
PURE_EXPR(id_manager::on_init<T>::func(static_cast<T*>(ptr)), void* ptr),
PURE_EXPR(id_manager::on_stop<T>::func(static_cast<T*>(ptr)), void* ptr),
});
}
// Object manager for emulated process. Multiple objects of specified arbitrary type are given unique IDs.
class idm
{
// Rules for ID allocation:
// 0) Individual ID counter may be specified for each type by defining 'using id_base = ...;'
// 1) If no id_base specified, void is assumed.
// 2) g_id[id_base] indicates next ID allocated in g_map.
// 3) g_map[id_base] contains the additional copy of object pointer.
// Custom hasher for ID values
struct id_hash_t final
{
std::size_t operator ()(u32 value) const
@ -90,82 +158,157 @@ namespace idm
}
};
using map_t = std::unordered_map<u32, id_data_t, id_hash_t>;
using map_type = std::unordered_map<u32, std::shared_ptr<void>, id_hash_t>;
// Can be called from the constructor called through make() or make_ptr() to get the ID of the object being created
inline u32 get_last_id()
// Type Index -> ID -> Object. Use global since only one process is supported atm.
static std::vector<map_type> g_map;
// Next ID for each category
static std::vector<u32> g_id;
static shared_mutex g_mutex;
static const auto& get_types()
{
extern thread_local u32 g_tls_last_id;
return g_tls_last_id;
return id_manager::typeinfo<idm>::get();
}
// Remove all objects
void clear();
// Internal
bool check(u32 in_id, id_type_index_t type);
// Check if an ID of specified type exists
template<typename T>
bool check(u32 id)
static inline u32 get_type()
{
return check(id_traits<T>::in_id(id), get_id_type_index<T>());
return id_manager::typeinfo<idm>::get_index<T>();
}
// Check if an ID exists and return its type or nullptr
const std::type_info* get_type(u32 raw_id);
// Internal
template<typename T, typename Ptr>
std::shared_ptr<T> add(Ptr&& get_ptr)
template<typename T>
static inline u32 get_tag()
{
extern shared_mutex g_mutex;
extern idm::map_t g_map;
extern u32 g_last_raw_id;
extern thread_local u32 g_tls_last_id;
return get_type<typename id_manager::id_traits<T>::tag>();
}
// Prepares new ID, returns nullptr if out of resources
static map_type::pointer allocate_id(u32 tag, u32 min, u32 max)
{
// Check all IDs starting from "next id"
for (u32 i = 0; i <= max - min; i++)
{
// Fix current ID (wrap around)
if (g_id[tag] < min || g_id[tag] > max) g_id[tag] = min;
// Get ID
const auto r = g_map[tag].emplace(g_id[tag]++, nullptr);
if (r.second)
{
return &*r.first;
}
}
// Nothing found
return nullptr;
}
// Deallocate ID, returns object
static std::shared_ptr<void> deallocate_id(u32 tag, u32 id)
{
const auto found = g_map[tag].find(id);
if (found == g_map[tag].end()) return nullptr;
auto ptr = std::move(found->second);
g_map[tag].erase(found);
return ptr;
}
// Allocate new ID and construct it from the provider()
template<typename T, typename F, typename = std::result_of_t<F()>>
static map_type::pointer create_id(F&& provider)
{
std::lock_guard<shared_mutex> lock(g_mutex);
for (u32 raw_id = g_last_raw_id; (raw_id = id_traits<T>::next_id(raw_id)); /**/)
if (auto place = allocate_id(get_tag<T>(), id_manager::id_traits<T>::min, id_manager::id_traits<T>::max))
{
if (g_map.find(raw_id) != g_map.end()) continue;
try
{
// Get object, write it
place->second = provider();
// Update ID storage if available
if (const u32* id = id_manager::id_storage<T>::get(static_cast<T*>(place->second.get())))
{
*const_cast<u32*>(id) = place->first;
}
g_tls_last_id = id_traits<T>::out_id(raw_id);
std::shared_ptr<T> ptr = get_ptr();
g_map.emplace(raw_id, id_data_t(ptr));
if (raw_id < 0x80000000) g_last_raw_id = raw_id;
return ptr;
return &*g_map[get_type<T>()].emplace(*place).first;
}
catch (...)
{
deallocate_id(get_tag<T>(), place->first);
throw;
}
}
return nullptr;
}
// Add a new ID of specified type with specified constructor arguments (returns object or nullptr)
template<typename T, typename... Args>
std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> make_ptr(Args&&... args)
// Remove ID and return object
static std::shared_ptr<void> delete_id(u32 type, u32 tag, u32 id)
{
if (auto ptr = add<T>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...))))
std::lock_guard<shared_mutex> lock(g_mutex);
auto&& ptr = deallocate_id(tag, id);
g_map[type].erase(id);
return ptr;
}
public:
// Initialize object manager
static void init()
{
g_map.resize(get_types().size(), {});
g_id.resize(get_types().size(), 0);
}
// Remove all objects
static void clear()
{
// Call recorded finalization functions for all IDs
for (std::size_t i = 0; i < g_map.size(); i++)
{
id_aux_initialize(ptr.get());
return ptr;
for (auto& id : g_map[i])
{
get_types()[i].on_stop(id.second.get());
}
g_map[i].clear();
g_id[i] = 0;
}
}
// Add a new ID of specified type with specified constructor arguments (returns object or nullptr)
template<typename T, typename Make = T, typename... Args>
static std::enable_if_t<std::is_constructible<Make, Args...>::value, std::shared_ptr<T>> make_ptr(Args&&... args)
{
if (auto pair = create_id<T>(WRAP_EXPR(std::make_shared<Make>(std::forward<Args>(args)...))))
{
id_manager::on_init<T>::func(static_cast<T*>(pair->second.get()));
return{ pair->second, static_cast<T*>(pair->second.get()) };
}
return nullptr;
}
// Add a new ID of specified type with specified constructor arguments (returns id)
template<typename T, typename... Args>
std::enable_if_t<std::is_constructible<T, Args...>::value, u32> make(Args&&... args)
template<typename T, typename Make = T, typename... Args>
static std::enable_if_t<std::is_constructible<Make, Args...>::value, u32> make(Args&&... args)
{
if (auto ptr = add<T>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...))))
if (auto pair = create_id<T>(WRAP_EXPR(std::make_shared<Make>(std::forward<Args>(args)...))))
{
id_aux_initialize(ptr.get());
return get_last_id();
id_manager::on_init<T>::func(static_cast<T*>(pair->second.get()));
return pair->first;
}
throw EXCEPTION("Out of IDs ('%s')", typeid(T).name());
@ -173,93 +316,118 @@ namespace idm
// Add a new ID for an existing object provided (returns new id)
template<typename T>
u32 import(const std::shared_ptr<T>& ptr)
static u32 import_existing(const std::shared_ptr<T>& ptr)
{
static const auto size = sizeof(T); // forbid forward declarations
if (add<T>(WRAP_EXPR(ptr)))
if (auto pair = create_id<T>(WRAP_EXPR(ptr)))
{
id_aux_initialize(ptr.get());
return get_last_id();
id_manager::on_init<T>::func(static_cast<T*>(pair->second.get()));
return pair->first;
}
throw EXCEPTION("Out of IDs ('%s')", typeid(T).name());
}
// Internal
std::shared_ptr<void> get(u32 in_id, id_type_index_t type);
// Get ID of specified type
template<typename T>
std::shared_ptr<T> get(u32 id)
// Add a new ID for an object returned by provider()
template<typename T, typename F, typename = std::result_of_t<F()>>
static std::shared_ptr<T> import(F&& provider)
{
return std::static_pointer_cast<T>(get(id_traits<T>::in_id(id), get_id_type_index<T>()));
}
// Internal
idm::map_t get_all(id_type_index_t type);
// Get all IDs of specified type T (unsorted)
template<typename T>
std::vector<std::shared_ptr<T>> get_all()
{
std::vector<std::shared_ptr<T>> result;
for (auto& id : get_all(get_id_type_index<T>()))
if (auto pair = create_id<T>(std::forward<F>(provider)))
{
result.emplace_back(std::static_pointer_cast<T>(id.second.data));
}
return result;
}
std::shared_ptr<void> withdraw(u32 in_id, id_type_index_t type);
// Remove the ID created with type T
template<typename T>
bool remove(u32 id)
{
if (auto ptr = withdraw(id_traits<T>::in_id(id), get_id_type_index<T>()))
{
id_aux_finalize(static_cast<T*>(ptr.get()));
return true;
}
return false;
}
// Remove the ID created with type T and return it
template<typename T>
std::shared_ptr<T> withdraw(u32 id)
{
if (auto ptr = std::static_pointer_cast<T>(withdraw(id_traits<T>::in_id(id), get_id_type_index<T>())))
{
id_aux_finalize(ptr.get());
return ptr;
id_manager::on_init<T>::func(static_cast<T*>(pair->second.get()));
return { pair->second, static_cast<T*>(pair->second.get()) };
}
return nullptr;
}
u32 get_count(id_type_index_t type);
// Check whether ID exists
template<typename T>
static bool check(u32 id)
{
reader_lock lock(g_mutex);
return g_map[get_type<T>()].count(id) != 0;
}
// Get ID
template<typename T>
static std::shared_ptr<T> get(u32 id)
{
reader_lock lock(g_mutex);
const auto found = g_map[get_type<T>()].find(id);
if (found == g_map[get_type<T>()].end())
{
return nullptr;
}
return{ found->second, static_cast<T*>(found->second.get()) };
}
// Get all IDs (unsorted)
template<typename T>
static std::vector<std::shared_ptr<T>> get_all()
{
reader_lock lock(g_mutex);
std::vector<std::shared_ptr<T>> result;
for (auto& id : g_map[get_type<T>()])
{
result.emplace_back(id.second, static_cast<T*>(id.second.get()));
}
return result;
}
// Remove the ID
template<typename T>
static bool remove(u32 id)
{
auto&& ptr = delete_id(get_type<T>(), get_tag<T>(), id);
if (ptr)
{
id_manager::on_stop<T>::func(static_cast<T*>(ptr.get()));
}
return ptr.operator bool();
}
// Remove the ID and return it
template<typename T>
static std::shared_ptr<T> withdraw(u32 id)
{
auto&& ptr = delete_id(get_type<T>(), get_tag<T>(), id);
if (ptr)
{
id_manager::on_stop<T>::func(static_cast<T*>(ptr.get()));
}
return{ ptr, static_cast<T*>(ptr.get()) };
}
template<typename T>
u32 get_count()
static u32 get_count()
{
return get_count(get_id_type_index<T>());
reader_lock lock(g_mutex);
return ::size32(g_map[get_type<T>()]);
}
// Get sorted list of all IDs of specified type
template<typename T>
std::set<u32> get_set()
static std::set<u32> get_set()
{
reader_lock lock(g_mutex);
std::set<u32> result;
for (auto& id : get_all(get_id_type_index<T>()))
for (auto& id : g_map[get_type<T>()])
{
result.emplace(id_traits<T>::out_id(id.first));
result.emplace(id.first);
}
return result;
@ -267,188 +435,234 @@ namespace idm
// Get sorted map (ID value -> ID data) of all IDs of specified type
template<typename T>
std::map<u32, std::shared_ptr<T>> get_map()
static std::map<u32, std::shared_ptr<T>> get_map()
{
reader_lock lock(g_mutex);
std::map<u32, std::shared_ptr<T>> result;
for (auto& id : get_all(get_id_type_index<T>()))
for (auto& id : g_map[get_type<T>()])
{
result[id_traits<T>::out_id(id.first)] = std::static_pointer_cast<T>(id.second.data);
result[id.first] = { id.second, static_cast<T*>(id.second.get()) };
}
return result;
}
}
};
// Fixed Object Manager
// allows to manage shared objects of any specified type, but only one object per type;
// object are deleted when the emulation is stopped
namespace fxm
// Object manager for emulated process. One unique object per type, or zero.
class fxm
{
// Custom hasher for aligned pointer values
struct hash_t final
// Type Index -> Object. Use global since only one process is supported atm.
static std::vector<std::shared_ptr<void>> g_map;
static shared_mutex g_mutex;
static inline const auto& get_types()
{
std::size_t operator()(id_type_index_t value) const
{
return reinterpret_cast<std::size_t>(value) >> 3;
}
};
return id_manager::typeinfo<fxm>::get();
}
using map_t = std::unordered_map<id_type_index_t, std::shared_ptr<void>, hash_t>;
// Remove all objects
void clear();
// Internal (returns old and new pointers)
template<typename T, bool Always, typename Ptr>
std::pair<std::shared_ptr<T>, std::shared_ptr<T>> add(Ptr&& get_ptr)
template<typename T>
static inline u32 get_type()
{
extern shared_mutex g_mutex;
extern fxm::map_t g_map;
return id_manager::typeinfo<fxm>::get_index<T>();
}
static std::shared_ptr<void> remove(u32 type)
{
std::lock_guard<shared_mutex> lock(g_mutex);
auto& item = g_map[get_id_type_index<T>()];
return std::move(g_map[type]);
}
if (Always || !item)
public:
// Initialize object manager
static void init()
{
g_map.resize(get_types().size(), {});
}
// Remove all objects
static void clear()
{
// Call recorded finalization functions for all IDs
for (std::size_t i = 0; i < g_map.size(); i++)
{
std::shared_ptr<T> old = std::static_pointer_cast<T>(std::move(item));
std::shared_ptr<T> ptr = get_ptr();
if (g_map[i])
{
get_types()[i].on_stop(g_map[i].get());
}
// Set new object
item = ptr;
return{ std::move(old), std::move(ptr) };
}
else
{
return{ std::static_pointer_cast<T>(item), nullptr };
g_map[i].reset();
}
}
// Create the object (returns nullptr if it already exists)
template<typename T, typename... Args>
std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> make(Args&&... args)
template<typename T, typename Make = T, typename... Args>
static std::enable_if_t<std::is_constructible<Make, Args...>::value, std::shared_ptr<T>> make(Args&&... args)
{
auto pair = add<T, false>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...)));
if (pair.second)
std::shared_ptr<T> ptr;
{
id_aux_initialize(pair.second.get());
std::lock_guard<shared_mutex> lock(g_mutex);
if (!g_map[get_type<T>()])
{
ptr = std::make_shared<Make>(std::forward<Args>(args)...);
g_map[get_type<T>()] = ptr;
}
}
return std::move(pair.second);
if (ptr)
{
id_manager::on_init<T>::func(ptr.get());
}
return ptr;
}
// Create the object unconditionally (old object will be removed if it exists)
template<typename T, typename... Args>
std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> make_always(Args&&... args)
template<typename T, typename Make = T, typename... Args>
static std::enable_if_t<std::is_constructible<Make, Args...>::value, std::shared_ptr<T>> make_always(Args&&... args)
{
auto pair = add<T, true>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...)));
if (pair.first)
std::shared_ptr<T> ptr;
std::shared_ptr<void> old;
{
id_aux_finalize(pair.first.get());
std::lock_guard<shared_mutex> lock(g_mutex);
old = std::move(g_map[get_type<T>()]);
ptr = std::make_shared<Make>(std::forward<Args>(args)...);
g_map[get_type<T>()] = ptr;
}
id_aux_initialize(pair.second.get());
return std::move(pair.second);
if (old)
{
id_manager::on_stop<T>::func(static_cast<T*>(old.get()));
}
id_manager::on_init<T>::func(ptr.get());
return ptr;
}
// Emplace the object returned by provider() and return it if no object exists
template<typename T, typename F>
auto import(F&& provider) -> decltype(static_cast<std::shared_ptr<T>>(provider()))
static auto import(F&& provider) -> decltype(static_cast<std::shared_ptr<T>>(provider()))
{
static const auto size = sizeof(T); // forbid forward declarations
auto pair = add<T, false>(std::forward<F>(provider));
if (pair.second)
std::shared_ptr<T> ptr;
{
id_aux_initialize(pair.second.get());
std::lock_guard<shared_mutex> lock(g_mutex);
if (!g_map[get_type<T>()])
{
ptr = provider();
g_map[get_type<T>()] = ptr;
}
}
return std::move(pair.second);
if (ptr)
{
id_manager::on_init<T>::func(ptr.get());
}
return ptr;
}
// Emplace the object return by provider() (old object will be removed if it exists)
template<typename T, typename F>
auto import_always(F&& provider) -> decltype(static_cast<std::shared_ptr<T>>(provider()))
static auto import_always(F&& provider) -> decltype(static_cast<std::shared_ptr<T>>(provider()))
{
static const auto size = sizeof(T); // forbid forward declarations
auto pair = add<T, true>(std::forward<F>(provider));
if (pair.first)
std::shared_ptr<T> ptr;
std::shared_ptr<void> old;
{
id_aux_finalize(pair.first.get());
std::lock_guard<shared_mutex> lock(g_mutex);
old = std::move(g_map[get_type<T>()]);
ptr = provider();
g_map[get_type<T>()] = ptr;
}
id_aux_initialize(pair.second.get());
return std::move(pair.second);
if (old)
{
id_manager::on_stop<T>::func(static_cast<T*>(old.get()));
}
id_manager::on_init<T>::func(ptr.get());
return ptr;
}
// Get the object unconditionally (create an object if it doesn't exist)
template<typename T, typename... Args>
std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> get_always(Args&&... args)
template<typename T, typename Make = T, typename... Args>
static std::enable_if_t<std::is_constructible<Make, Args...>::value, std::shared_ptr<T>> get_always(Args&&... args)
{
auto pair = add<T, false>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...)));
if (pair.second)
std::shared_ptr<T> ptr;
{
id_aux_initialize(pair.second.get());
return std::move(pair.second);
std::lock_guard<shared_mutex> lock(g_mutex);
if (auto& value = g_map[get_type<T>()])
{
return{ value, static_cast<T*>(value.get()) };
}
else
{
ptr = std::make_shared<Make>(std::forward<Args>(args)...);
g_map[get_type<T>()] = ptr;
}
}
return std::move(pair.first);
id_manager::on_init<T>::func(ptr.get());
return ptr;
}
// Internal
bool check(id_type_index_t type);
// Check whether the object exists
template<typename T>
bool check()
static bool check()
{
return check(get_id_type_index<T>());
}
reader_lock lock(g_mutex);
// Internal
std::shared_ptr<void> get(id_type_index_t type);
return g_map[get_type<T>()].operator bool();
}
// Get the object (returns nullptr if it doesn't exist)
template<typename T>
std::shared_ptr<T> get()
static std::shared_ptr<T> get()
{
return std::static_pointer_cast<T>(get(get_id_type_index<T>()));
}
reader_lock lock(g_mutex);
// Internal
std::shared_ptr<void> withdraw(id_type_index_t type);
auto& ptr = g_map[get_type<T>()];
return{ ptr, static_cast<T*>(ptr.get()) };
}
// Delete the object
template<typename T>
bool remove()
static bool remove()
{
if (auto ptr = withdraw(get_id_type_index<T>()))
auto&& ptr = remove(get_type<T>());
if (ptr)
{
id_aux_finalize(static_cast<T*>(ptr.get()));
return true;
id_manager::on_stop<T>::func(static_cast<T*>(ptr.get()));
}
return false;
return ptr.operator bool();
}
// Delete the object and return it
template<typename T>
std::shared_ptr<T> withdraw()
static std::shared_ptr<T> withdraw()
{
if (auto ptr = std::static_pointer_cast<T>(withdraw(get_id_type_index<T>())))
auto&& ptr = remove(get_type<T>());
if (ptr)
{
id_aux_finalize(ptr.get());
return ptr;
id_manager::on_stop<T>::func(static_cast<T*>(ptr.get()));
}
return nullptr;
return{ ptr, static_cast<T*>(ptr.get()) };
}
}
};

View file

@ -1,92 +1,62 @@
#include "stdafx.h"
#include "config.h"
#include "events.h"
#include "state.h"
#include "Utilities/Config.h"
#include "Utilities/AutoPause.h"
#include "Utilities/event.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/GameInfo.h"
#include "Emu/SysCalls/ModuleManager.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/Cell/PPUCallback.h"
#include "Emu/Cell/PPUOpcodes.h"
#include "Emu/Cell/SPUThread.h"
#include "Emu/Cell/PPUInstrTable.h"
#include "Emu/FS/vfsFile.h"
#include "Emu/FS/vfsLocalFile.h"
#include "Emu/FS/vfsDeviceLocalFile.h"
#include "Emu/Cell/lv2/sys_sync.h"
#include "Emu/CPU/CPUThreadManager.h"
#include "Emu/SysCalls/Callback.h"
#include "Emu/IdManager.h"
#include "Emu/Io/Pad.h"
#include "Emu/Io/Keyboard.h"
#include "Emu/Io/Mouse.h"
#include "Emu/RSX/GSManager.h"
#include "Emu/Audio/AudioManager.h"
#include "Emu/FS/VFS.h"
#include "Emu/Event.h"
#include "Emu/RSX/GSRender.h"
#include "Loader/PSF.h"
#include "Loader/ELF64.h"
#include "Loader/ELF32.h"
#include "Loader/ELF.h"
#include "../Crypto/unself.h"
using namespace PPU_instr;
cfg::bool_entry g_cfg_autostart(cfg::root.misc, "Always start after boot");
cfg::bool_entry g_cfg_autoexit(cfg::root.misc, "Exit RPCS3 when process finishes");
static const std::string& BreakPointsDBName = "BreakPoints.dat";
static const u16 bpdb_version = 0x1000;
std::string g_cfg_defaults;
// Draft (not used)
struct bpdb_header_t
{
le_t<u32> magic;
le_t<u32> version;
le_t<u32> count;
le_t<u32> marked;
extern cfg::string_entry g_cfg_vfs_dev_bdvd;
extern cfg::string_entry g_cfg_vfs_app_home;
// POD
bpdb_header_t() = default;
bpdb_header_t(u32 count, u32 marked)
: magic(*reinterpret_cast<const u32*>("BPDB"))
, version(0x00010000)
, count(count)
, marked(marked)
{
}
};
extern std::atomic<u32> g_thread_count;
extern atomic_t<u32> g_thread_count;
extern u64 get_system_time();
extern void finalize_psv_modules();
namespace rpcs3
{
event<void>& on_run() { static event<void> on_run; return on_run; }
event<void>& on_stop() { static event<void> on_stop; return on_stop; }
event<void>& on_pause() { static event<void> on_pause; return on_pause; }
event<void>& on_resume() { static event<void> on_resume; return on_resume; }
}
Emulator::Emulator()
: m_status(Stopped)
, m_mode(DisAsm)
, m_rsx_callback(0)
, m_cpu_thr_stop(0)
, m_thread_manager(new CPUThreadManager())
, m_pad_manager(new PadManager())
, m_keyboard_manager(new KeyboardManager())
, m_mouse_manager(new MouseManager())
, m_gs_manager(new GSManager())
, m_audio_manager(new AudioManager())
, m_callback_manager(new CallbackManager())
, m_event_manager(new EventManager())
, m_module_manager(new ModuleManager())
, m_vfs(new VFS())
{
m_loader.register_handler(new loader::handlers::elf32);
m_loader.register_handler(new loader::handlers::elf64);
}
void Emulator::Init()
{
rpcs3::config.load();
rpcs3::oninit();
idm::init();
fxm::init();
// Reset defaults, cache them
cfg::root.from_default();
g_cfg_defaults = cfg::root.to_string();
// Reload global configuration
cfg::root.from_string(fs::file(fs::get_config_dir() + "/config.yml", fs::read + fs::create).to_string());
}
void Emulator::SetPath(const std::string& path, const std::string& elf_path)
@ -95,31 +65,6 @@ void Emulator::SetPath(const std::string& path, const std::string& elf_path)
m_elf_path = elf_path;
}
void Emulator::SetTitleID(const std::string& id)
{
m_title_id = id;
}
void Emulator::SetTitle(const std::string& title)
{
m_title = title;
}
void Emulator::CreateConfig(const std::string& name)
{
const std::string& path = fs::get_config_dir() + "data/" + name;
const std::string& ini_file = path + "/settings.ini";
if (!fs::is_dir("data"))
fs::create_dir("data");
if (!fs::is_dir(path))
fs::create_dir(path);
if (!fs::is_file(ini_file))
rpcs3::config_t{ ini_file }.save();
}
bool Emulator::BootGame(const std::string& path, bool direct)
{
static const char* boot_list[] =
@ -158,141 +103,161 @@ bool Emulator::BootGame(const std::string& path, bool direct)
void Emulator::Load()
{
m_status = Ready;
Stop();
if (!fs::is_file(m_path))
try
{
m_status = Stopped;
return;
}
Init();
const std::string& elf_dir = fs::get_parent_dir(m_path);
if (IsSelf(m_path))
{
const std::size_t elf_ext_pos = m_path.find_last_of('.');
const std::string& elf_ext = fmt::toupper(m_path.substr(elf_ext_pos != -1 ? elf_ext_pos : m_path.size()));
const std::string& elf_name = m_path.substr(elf_dir.size());
if (elf_name.compare(elf_name.find_last_of("/\\", -1, 2) + 1, 9, "EBOOT.BIN", 9) == 0)
if (!fs::is_file(m_path))
{
m_path.erase(m_path.size() - 9, 1); // change EBOOT.BIN to BOOT.BIN
LOG_ERROR(LOADER, "File not found: %s", m_path);
return;
}
else if (elf_ext == ".SELF" || elf_ext == ".SPRX")
const std::string& elf_dir = fs::get_parent_dir(m_path);
if (IsSelf(m_path))
{
m_path.erase(m_path.size() - 4, 1); // change *.self to *.elf, *.sprx to *.prx
const std::size_t elf_ext_pos = m_path.find_last_of('.');
const std::string& elf_ext = fmt::to_upper(m_path.substr(elf_ext_pos != -1 ? elf_ext_pos : m_path.size()));
const std::string& elf_name = m_path.substr(elf_dir.size());
if (elf_name.compare(elf_name.find_last_of("/\\", -1, 2) + 1, 9, "EBOOT.BIN", 9) == 0)
{
m_path.erase(m_path.size() - 9, 1); // change EBOOT.BIN to BOOT.BIN
}
else if (elf_ext == ".SELF" || elf_ext == ".SPRX")
{
m_path.erase(m_path.size() - 4, 1); // change *.self to *.elf, *.sprx to *.prx
}
else
{
m_path += ".decrypted.elf";
}
if (!DecryptSelf(m_path, elf_dir + elf_name))
{
LOG_ERROR(LOADER, "Failed to decrypt %s", elf_dir + elf_name);
return;
}
}
ResetInfo();
LOG_NOTICE(LOADER, "Path: %s", m_path);
// Load custom config
if (fs::file cfg_file{ m_path + ".yml" })
{
LOG_NOTICE(LOADER, "Custom config: %s.yml", m_path);
cfg::root.from_string(cfg_file.to_string());
}
const fs::file elf_file(m_path);
ppu_exec_loader ppu_exec;
ppu_prx_loader ppu_prx;
spu_exec_loader spu_exec;
arm_exec_loader arm_exec;
if (!elf_file)
{
LOG_ERROR(LOADER, "Failed to open %s", m_path);
return;
}
else if (ppu_exec.open(elf_file) == elf_error::ok)
{
// PS3 executable
m_status = Ready;
vm::ps3::init();
if (m_elf_path.empty())
{
m_elf_path = "/host_root/" + m_path;
LOG_NOTICE(LOADER, "Elf path: %s", m_elf_path);
}
// Load PARAM.SFO
m_psf = psf::load_object(fs::file(elf_dir + "/../PARAM.SFO"));
m_title = psf::get_string(m_psf, "TITLE", m_path);
m_title_id = psf::get_string(m_psf, "TITLE_ID");
LOG_NOTICE(LOADER, "Title: %s", GetTitle());
LOG_NOTICE(LOADER, "Serial: %s", GetTitleID());
LOG_NOTICE(LOADER, "");
LOG_NOTICE(LOADER, "Used configuration:\n%s\n", cfg::root.to_string());
// Mount /dev_bdvd/
if (g_cfg_vfs_dev_bdvd.size() == 0 && fs::is_file(elf_dir + "/../../PS3_DISC.SFB"))
{
const auto dir_list = fmt::split(elf_dir, { "/", "\\" });
// Check latest two directories
if (dir_list.size() >= 2 && dir_list.back() == "USRDIR" && *(dir_list.end() - 2) == "PS3_GAME")
{
g_cfg_vfs_dev_bdvd = elf_dir.substr(0, elf_dir.length() - 15);
}
else
{
g_cfg_vfs_dev_bdvd = elf_dir + "/../../";
}
}
// Mount /app_home/
if (g_cfg_vfs_app_home.size() == 0)
{
g_cfg_vfs_app_home = elf_dir + '/';
}
vfs::dump();
ppu_exec.load();
Emu.GetCallbackManager().Init();
fxm::import<GSRender>(PURE_EXPR(Emu.GetCallbacks().get_gs_render())); // TODO: must be created in appropriate sys_rsx syscall
}
else if (ppu_prx.open(elf_file) == elf_error::ok)
{
// PPU PRX (experimental)
m_status = Ready;
vm::ps3::init();
ppu_prx.load();
GetCallbackManager().Init();
}
else if (spu_exec.open(elf_file) == elf_error::ok)
{
// SPU executable (experimental)
m_status = Ready;
vm::ps3::init();
spu_exec.load();
}
else if (arm_exec.open(elf_file) == elf_error::ok)
{
// ARMv7 executable
m_status = Ready;
vm::psv::init();
arm_exec.load();
}
else
{
m_path += ".decrypted.elf";
}
LOG_ERROR(LOADER, "Invalid or unsupported file format: %s", m_path);
if (!DecryptSelf(m_path, elf_dir + elf_name))
{
m_status = Stopped;
LOG_WARNING(LOADER, "** ppu_exec_loader -> %s", bijective_find<elf_error>(ppu_exec, "???"));
LOG_WARNING(LOADER, "** ppu_prx_loader -> %s", bijective_find<elf_error>(ppu_prx, "???"));
LOG_WARNING(LOADER, "** spu_exec_loader -> %s", bijective_find<elf_error>(spu_exec, "???"));
LOG_WARNING(LOADER, "** arm_exec_loader -> %s", bijective_find<elf_error>(arm_exec, "???"));
return;
}
debug::autopause::reload();
SendDbgCommand(DID_READY_EMU);
if (g_cfg_autostart) Run();
}
ResetInfo();
GetVFS().Init(elf_dir);
LOG_NOTICE(LOADER, "Loading '%s'...", m_path.c_str());
// /dev_bdvd/ mounting
vfsFile f("/app_home/../dev_bdvd.path");
if (f.IsOpened())
catch (const std::exception& e)
{
// load specified /dev_bdvd/ directory and mount it
std::string bdvd;
bdvd.resize(f.GetSize());
f.Read(&bdvd[0], bdvd.size());
Emu.GetVFS().Mount("/dev_bdvd/", bdvd, new vfsDeviceLocalFile());
LOG_FATAL(LOADER, "%s thrown: %s", typeid(e).name(), e.what());
Stop();
}
else if (fs::is_file(elf_dir + "/../../PS3_DISC.SFB")) // guess loading disc game
{
const auto dir_list = fmt::split(elf_dir, { "/", "\\" });
// check latest two directories
if (dir_list.size() >= 2 && dir_list.back() == "USRDIR" && *(dir_list.end() - 2) == "PS3_GAME")
{
// mount detected /dev_bdvd/ directory
Emu.GetVFS().Mount("/dev_bdvd/", elf_dir.substr(0, elf_dir.length() - 16), new vfsDeviceLocalFile());
}
}
LOG_NOTICE(LOADER, "");
LOG_NOTICE(LOADER, "Mount info:");
for (uint i = 0; i < GetVFS().m_devices.size(); ++i)
{
LOG_NOTICE(LOADER, "%s -> %s", GetVFS().m_devices[i]->GetPs3Path().c_str(), GetVFS().m_devices[i]->GetLocalPath().c_str());
}
LOG_NOTICE(LOADER, "");
f.Open("/app_home/../PARAM.SFO");
const auto& psf = psf::load(f.VRead<char>());
std::string title = psf::get_string(psf, "TITLE");
std::string title_id = psf::get_string(psf, "TITLE_ID");
LOG_NOTICE(LOADER, "Title: %s", title.c_str());
LOG_NOTICE(LOADER, "Serial: %s", title_id.c_str());
title.length() ? SetTitle(title) : SetTitle(m_path);
SetTitleID(title_id);
rpcs3::state.config = rpcs3::config;
// load custom config
if (!rpcs3::config.misc.use_default_ini.value())
{
if (title_id.size())
{
title_id = title_id.substr(0, 4) + "-" + title_id.substr(4, 5);
CreateConfig(title_id);
rpcs3::config_t custom_config { fs::get_config_dir() + "data/" + title_id + "/settings.ini" };
custom_config.load();
rpcs3::state.config = custom_config;
}
}
LOG_NOTICE(LOADER, "Used configuration: '%s'", rpcs3::state.config.path().c_str());
LOG_NOTICE(LOADER, "");
LOG_NOTICE(LOADER, rpcs3::state.config.to_string().c_str());
if (m_elf_path.empty())
{
GetVFS().GetDeviceLocal(m_path, m_elf_path);
LOG_NOTICE(LOADER, "Elf path: %s", m_elf_path);
LOG_NOTICE(LOADER, "");
}
f.Open(m_elf_path);
if (!f.IsOpened())
{
LOG_ERROR(LOADER, "Opening '%s' failed", m_path.c_str());
m_status = Stopped;
return;
}
if (!m_loader.load(f))
{
LOG_ERROR(LOADER, "Loading '%s' failed", m_path.c_str());
LOG_NOTICE(LOADER, "");
m_status = Stopped;
vm::close();
return;
}
LoadPoints(fs::get_config_dir() + BreakPointsDBName);
GetGSManager().Init();
GetCallbackManager().Init();
GetAudioManager().Init();
GetEventManager().Init();
SendDbgCommand(DID_READY_EMU);
}
void Emulator::Run()
@ -311,7 +276,7 @@ void Emulator::Run()
return;
}
rpcs3::onstart();
rpcs3::on_run()();
SendDbgCommand(DID_START_EMU);
@ -319,7 +284,12 @@ void Emulator::Run()
m_pause_amend_time = 0;
m_status = Running;
GetCPU().Exec();
for (auto& thread : get_all_cpu_threads())
{
thread->state -= cpu_state::stop;
thread->safe_notify();
}
SendDbgCommand(DID_STARTED_EMU);
}
@ -327,15 +297,15 @@ bool Emulator::Pause()
{
const u64 start = get_system_time();
// try to set Paused status
if (!sync_bool_compare_and_swap(&m_status, Running, Paused))
// Try to pause
if (!m_status.compare_and_swap_test(Running, Paused))
{
return false;
}
rpcs3::onpause();
rpcs3::on_pause()();
// update pause start time
// Update pause start time
if (m_pause_start_time.exchange(start))
{
LOG_ERROR(GENERAL, "Emulator::Pause() error: concurrent access");
@ -343,9 +313,9 @@ bool Emulator::Pause()
SendDbgCommand(DID_PAUSE_EMU);
for (auto& t : GetCPU().GetAllThreads())
for (auto& thread : get_all_cpu_threads())
{
t->sleep(); // trigger status check
thread->state += cpu_state::dbg_global_pause;
}
SendDbgCommand(DID_PAUSED_EMU);
@ -355,17 +325,17 @@ bool Emulator::Pause()
void Emulator::Resume()
{
// get pause start time
// Get pause start time
const u64 time = m_pause_start_time.exchange(0);
// try to increment summary pause time
// Try to increment summary pause time
if (time)
{
m_pause_amend_time += get_system_time() - time;
}
// try to resume
if (!sync_bool_compare_and_swap(&m_status, Paused, Running))
// Try to resume
if (!m_status.compare_and_swap_test(Paused, Running))
{
return;
}
@ -377,41 +347,36 @@ void Emulator::Resume()
SendDbgCommand(DID_RESUME_EMU);
for (auto& t : GetCPU().GetAllThreads())
for (auto& thread : get_all_cpu_threads())
{
t->awake(); // untrigger status check and signal
thread->state -= cpu_state::dbg_global_pause;
thread->safe_notify();
}
rpcs3::onstart();
rpcs3::on_resume()();
SendDbgCommand(DID_RESUMED_EMU);
}
extern std::map<u32, std::string> g_armv7_dump;
void Emulator::Stop()
{
LOG_NOTICE(GENERAL, "Stopping emulator...");
if (sync_lock_test_and_set(&m_status, Stopped) == Stopped)
if (m_status.exchange(Stopped) == Stopped)
{
return;
}
rpcs3::onstop();
LOG_NOTICE(GENERAL, "Stopping emulator...");
rpcs3::on_stop()();
SendDbgCommand(DID_STOP_EMU);
{
LV2_LOCK;
// notify all threads
for (auto& t : GetCPU().GetAllThreads())
for (auto& thread : get_all_cpu_threads())
{
std::lock_guard<std::mutex> lock(t->mutex);
t->sleep(); // trigger status check
t->cv.notify_one(); // signal
thread->state += cpu_state::dbg_global_stop;
thread->safe_notify();
}
}
@ -431,86 +396,34 @@ void Emulator::Stop()
LOG_NOTICE(GENERAL, "Objects cleared...");
finalize_psv_modules();
for (auto& v : decltype(g_armv7_dump)(std::move(g_armv7_dump)))
{
LOG_NOTICE(ARMv7, "%s", v.second);
}
m_rsx_callback = 0;
m_cpu_thr_stop = 0;
// TODO: check finalization order
SavePoints(fs::get_config_dir() + BreakPointsDBName);
m_break_points.clear();
m_marked_points.clear();
GetVFS().UnMountAll();
GetGSManager().Close();
GetAudioManager().Close();
GetEventManager().Clear();
GetCPU().Close();
GetPadManager().Close();
GetKeyboardManager().Close();
GetMouseManager().Close();
GetCallbackManager().Clear();
GetModuleManager().Close();
RSXIOMem.Clear();
vm::close();
SendDbgCommand(DID_STOPPED_EMU);
}
void Emulator::SavePoints(const std::string& path)
{
const u32 break_count = size32(m_break_points);
const u32 marked_count = size32(m_marked_points);
fs::file(path, fom::rewrite)
.write(bpdb_version)
.write(break_count)
.write(marked_count)
.write(m_break_points)
.write(m_marked_points);
}
bool Emulator::LoadPoints(const std::string& path)
{
if (fs::file f{ path })
if (g_cfg_autoexit)
{
u16 version;
u32 break_count;
u32 marked_count;
if (!f.read(version) || !f.read(break_count) || !f.read(marked_count))
{
LOG_ERROR(LOADER, "BP file '%s' is broken (length=0x%llx)", path, f.size());
return false;
}
if (version != bpdb_version)
{
LOG_ERROR(LOADER, "BP file '%s' has unsupported version (version=0x%x)", path, version);
return false;
}
m_break_points.resize(break_count);
m_marked_points.resize(marked_count);
if (!f.read(m_break_points) || !f.read(m_marked_points))
{
LOG_ERROR(LOADER, "'BP file %s' is broken (length=0x%llx, break_count=%u, marked_count=%u)", path, f.size(), break_count, marked_count);
return false;
}
return true;
GetCallbacks().exit();
}
else
{
Init();
}
return false;
}
Emulator Emu;
DECLARE(idm::g_map);
DECLARE(idm::g_id);
DECLARE(idm::g_mutex);
DECLARE(fxm::g_map);
DECLARE(fxm::g_mutex);
#ifndef _MSC_VER
constexpr std::pair<elf_error, const char*> bijective<elf_error, const char*>::map[];
constexpr std::pair<_log::level, const char*> bijective<_log::level, const char*>::map[];
constexpr std::pair<rsx::shader_language, const char*> bijective<rsx::shader_language, const char*>::map[];
#endif

View file

@ -1,7 +1,8 @@
#pragma once
#include "Loader/Loader.h"
#include "VFS.h"
#include "DbgCommand.h"
#include "Loader/PSF.h"
enum class frame_type;
@ -9,11 +10,12 @@ struct EmuCallbacks
{
std::function<void(std::function<void()>)> call_after;
std::function<void()> process_events;
std::function<void(DbgCommand, class CPUThread*)> send_dbg_command;
std::function<std::unique_ptr<class KeyboardHandlerBase>()> get_kb_handler;
std::function<std::unique_ptr<class MouseHandlerBase>()> get_mouse_handler;
std::function<std::unique_ptr<class PadHandlerBase>()> get_pad_handler;
std::function<std::unique_ptr<class GSFrameBase>(frame_type)> get_gs_frame;
std::function<void()> exit;
std::function<void(DbgCommand, class cpu_thread*)> send_dbg_command;
std::function<std::shared_ptr<class KeyboardHandlerBase>()> get_kb_handler;
std::function<std::shared_ptr<class MouseHandlerBase>()> get_mouse_handler;
std::function<std::shared_ptr<class PadHandlerBase>()> get_pad_handler;
std::function<std::unique_ptr<class GSFrameBase>(frame_type, size2i)> get_gs_frame;
std::function<std::shared_ptr<class GSRender>()> get_gs_render;
std::function<std::shared_ptr<class AudioThread>()> get_audio;
std::function<std::shared_ptr<class MsgDialogBase>()> get_msg_dialog;
@ -31,17 +33,7 @@ enum Status : u32
// Emulation Stopped exception event
class EmulationStopped {};
class CPUThreadManager;
class PadManager;
class KeyboardManager;
class MouseManager;
class GSManager;
class AudioManager;
class CallbackManager;
class CPUThread;
class EventManager;
class ModuleManager;
struct VFS;
struct EmuInfo
{
@ -64,47 +56,24 @@ public:
class Emulator final
{
atomic_t<u32> m_status;
EmuCallbacks m_cb;
enum Mode
{
DisAsm,
InterpreterDisAsm,
Interpreter,
};
volatile u32 m_status;
uint m_mode;
atomic_t<u64> m_pause_start_time; // set when paused
atomic_t<u64> m_pause_amend_time; // increased when resumed
std::atomic<u64> m_pause_start_time; // set when paused
std::atomic<u64> m_pause_amend_time; // increased when resumed
u32 m_rsx_callback;
u32 m_cpu_thr_stop;
std::vector<u64> m_break_points;
std::vector<u64> m_marked_points;
std::mutex m_core_mutex;
std::unique_ptr<CPUThreadManager> m_thread_manager;
std::unique_ptr<PadManager> m_pad_manager;
std::unique_ptr<KeyboardManager> m_keyboard_manager;
std::unique_ptr<MouseManager> m_mouse_manager;
std::unique_ptr<GSManager> m_gs_manager;
std::unique_ptr<AudioManager> m_audio_manager;
std::unique_ptr<CallbackManager> m_callback_manager;
std::unique_ptr<EventManager> m_event_manager;
std::unique_ptr<ModuleManager> m_module_manager;
std::unique_ptr<VFS> m_vfs;
EmuInfo m_info;
loader::loader m_loader;
std::string m_path;
std::string m_elf_path;
std::string m_title_id;
std::string m_title;
psf::registry m_psf;
public:
Emulator();
@ -119,36 +88,15 @@ public:
return m_cb;
}
void SendDbgCommand(DbgCommand cmd, class CPUThread* thread = nullptr)
void SendDbgCommand(DbgCommand cmd, class cpu_thread* thread = nullptr)
{
if (m_cb.send_dbg_command) m_cb.send_dbg_command(cmd, thread);
}
// Returns a future object associated with the result of the function called from the GUI thread
template<typename F>
std::future<void> CallAfter(F&& func) const
// Call from the GUI thread
void CallAfter(std::function<void()>&& func) const
{
// Make "shared" promise to workaround std::function limitation
auto spr = std::make_shared<std::promise<void>>();
// Get future
std::future<void> future = spr->get_future();
// Run asynchronously in GUI thread
m_cb.call_after([spr = std::move(spr), task = std::forward<F>(func)]()
{
try
{
task();
spr->set_value();
}
catch (...)
{
spr->set_exception(std::current_exception());
}
});
return future;
return m_cb.call_after(std::move(func));
}
/** Set emulator mode to running unconditionnaly.
@ -160,10 +108,7 @@ public:
}
void Init();
void SetPath(const std::string& path, const std::string& elf_path = "");
void SetTitleID(const std::string& id);
void SetTitle(const std::string& title);
void CreateConfig(const std::string& name);
void SetPath(const std::string& path, const std::string& elf_path = {});
const std::string& GetPath() const
{
@ -180,24 +125,20 @@ public:
return m_title;
}
const psf::registry& GetPSF() const
{
return m_psf;
}
u64 GetPauseTime()
{
return m_pause_amend_time;
}
std::mutex& GetCoreMutex() { return m_core_mutex; }
CPUThreadManager& GetCPU() { return *m_thread_manager; }
PadManager& GetPadManager() { return *m_pad_manager; }
KeyboardManager& GetKeyboardManager() { return *m_keyboard_manager; }
MouseManager& GetMouseManager() { return *m_mouse_manager; }
GSManager& GetGSManager() { return *m_gs_manager; }
AudioManager& GetAudioManager() { return *m_audio_manager; }
CallbackManager& GetCallbackManager() { return *m_callback_manager; }
VFS& GetVFS() { return *m_vfs; }
std::vector<u64>& GetBreakPoints() { return m_break_points; }
std::vector<u64>& GetMarkedPoints() { return m_marked_points; }
EventManager& GetEventManager() { return *m_event_manager; }
ModuleManager& GetModuleManager() { return *m_module_manager; }
CallbackManager& GetCallbackManager()
{
return *m_callback_manager;
}
void ResetInfo()
{
@ -219,11 +160,6 @@ public:
m_info.m_primary_prio = prio;
}
void SetRSXCallback(u32 addr)
{
m_rsx_callback = addr;
}
void SetCPUThreadStop(u32 addr)
{
m_cpu_thr_stop = addr;
@ -238,7 +174,6 @@ public:
u32 GetPrimaryStackSize() { return m_info.m_primary_stacksize; }
s32 GetPrimaryPrio() { return m_info.m_primary_prio; }
u32 GetRSXCallback() const { return m_rsx_callback; }
u32 GetCPUThreadStop() const { return m_cpu_thr_stop; }
bool BootGame(const std::string& path, bool direct = false);
@ -249,9 +184,6 @@ public:
void Resume();
void Stop();
void SavePoints(const std::string& path);
bool LoadPoints(const std::string& path);
force_inline bool IsRunning() const { return m_status == Running; }
force_inline bool IsPaused() const { return m_status == Paused; }
force_inline bool IsStopped() const { return m_status == Stopped; }
@ -260,14 +192,4 @@ public:
extern Emulator Emu;
using lv2_lock_t = std::unique_lock<std::mutex>;
inline bool check_lv2_lock(lv2_lock_t& lv2_lock)
{
return lv2_lock.owns_lock() && lv2_lock.mutex() == &Emu.GetCoreMutex();
}
#define LV2_LOCK lv2_lock_t lv2_lock(Emu.GetCoreMutex())
#define LV2_DEFER_LOCK lv2_lock_t lv2_lock
#define CHECK_LV2_LOCK(x) if (!check_lv2_lock(x)) throw EXCEPTION("lv2_lock is invalid or not locked")
#define CHECK_EMU_STATUS if (Emu.IsStopped()) throw EmulationStopped{}

77
rpcs3/Emu/VFS.cpp Normal file
View file

@ -0,0 +1,77 @@
#include "stdafx.h"
#include "Utilities/Config.h"
#include "Emu/System.h"
#include "VFS.h"
cfg::string_entry g_cfg_vfs_emulator_dir(cfg::root.vfs, "$(EmulatorDir)"); // Default (empty): taken from fs::get_executable_dir()
cfg::string_entry g_cfg_vfs_dev_hdd0(cfg::root.vfs, "/dev_hdd0/", "$(EmulatorDir)dev_hdd0/");
cfg::string_entry g_cfg_vfs_dev_hdd1(cfg::root.vfs, "/dev_hdd1/", "$(EmulatorDir)dev_hdd1/");
cfg::string_entry g_cfg_vfs_dev_flash(cfg::root.vfs, "/dev_flash/", "$(EmulatorDir)dev_flash/");
cfg::string_entry g_cfg_vfs_dev_usb000(cfg::root.vfs, "/dev_usb000/", "$(EmulatorDir)dev_usb000/");
cfg::string_entry g_cfg_vfs_dev_bdvd(cfg::root.vfs, "/dev_bdvd/"); // Not mounted
cfg::string_entry g_cfg_vfs_app_home(cfg::root.vfs, "/app_home/"); // Not mounted
cfg::bool_entry g_cfg_vfs_allow_host_root(cfg::root.vfs, "Enable /host_root/", true);
void vfs::dump()
{
LOG_NOTICE(LOADER, "Mount info:");
LOG_NOTICE(LOADER, "/dev_hdd0/ -> %s", g_cfg_vfs_dev_hdd0.get());
LOG_NOTICE(LOADER, "/dev_hdd1/ -> %s", g_cfg_vfs_dev_hdd1.get());
LOG_NOTICE(LOADER, "/dev_flash/ -> %s", g_cfg_vfs_dev_flash.get());
LOG_NOTICE(LOADER, "/dev_usb/ -> %s", g_cfg_vfs_dev_usb000.get());
LOG_NOTICE(LOADER, "/dev_usb000/ -> %s", g_cfg_vfs_dev_usb000.get());
if (g_cfg_vfs_dev_bdvd.size()) LOG_NOTICE(LOADER, "/dev_bdvd/ -> %s", g_cfg_vfs_dev_bdvd.get());
if (g_cfg_vfs_app_home.size()) LOG_NOTICE(LOADER, "/app_home/ -> %s", g_cfg_vfs_app_home.get());
if (g_cfg_vfs_allow_host_root) LOG_NOTICE(LOADER, "/host_root/ -> .");
LOG_NOTICE(LOADER, "");
}
std::string vfs::get(const std::string& vpath)
{
const cfg::string_entry* vdir = nullptr;
std::size_t f_pos = vpath.find_first_not_of('/');
std::size_t start = 0;
// Compare vpath with device name
auto detect = [&](const auto& vdev) -> bool
{
const std::size_t size = ::size32(vdev) - 1; // Char array size
if (f_pos && f_pos != -1 && vpath.compare(f_pos - 1, size, vdev, size) == 0)
{
start = size;
return true;
}
return false;
};
if (g_cfg_vfs_allow_host_root && detect("/host_root/"))
return vpath.substr(start); // Accessing host FS directly
else if (detect("/dev_hdd0/"))
vdir = &g_cfg_vfs_dev_hdd0;
else if (detect("/dev_hdd1/"))
vdir = &g_cfg_vfs_dev_hdd1;
else if (detect("/dev_flash/"))
vdir = &g_cfg_vfs_dev_flash;
else if (detect("/dev_usb000/"))
vdir = &g_cfg_vfs_dev_usb000;
else if (detect("/dev_usb/"))
vdir = &g_cfg_vfs_dev_usb000;
else if (detect("/dev_bdvd/"))
vdir = &g_cfg_vfs_dev_bdvd;
else if (detect("/app_home/"))
vdir = &g_cfg_vfs_app_home;
// Return empty path if not mounted
if (!vdir || !start)
{
LOG_WARNING(GENERAL, "vfs::get() failed for %s", vpath);
return{};
}
// Replace $(EmulatorDir), concatenate
return fmt::replace_all(*vdir, "$(EmulatorDir)", g_cfg_vfs_emulator_dir.size() == 0 ? fs::get_executable_dir() : g_cfg_vfs_emulator_dir) + vpath.substr(start);
}

10
rpcs3/Emu/VFS.h Normal file
View file

@ -0,0 +1,10 @@
#pragma once
namespace vfs
{
// Print mounted directories
void dump();
// Convert PS3/PSV path to fs-compatible path
std::string get(const std::string& vpath);
}

View file

@ -1,10 +0,0 @@
#include "stdafx.h"
#include "events.h"
namespace rpcs3
{
event<void> oninit;
event<void> onstart;
event<void> onstop;
event<void> onpause;
}

View file

@ -1,11 +0,0 @@
#pragma once
#include <Utilities/event.h>
namespace rpcs3
{
extern event<void> oninit;
extern event<void> onstart;
extern event<void> onstop;
extern event<void> onpause;
}

View file

@ -1,7 +0,0 @@
#include "stdafx.h"
#include "state.h"
namespace rpcs3
{
state_t state;
}

View file

@ -1,16 +0,0 @@
#pragma once
#include "config.h"
namespace rpcs3
{
struct state_t
{
config_t config;
std::string path_to_elf;
std::string virtual_path_to_elf;
};
extern state_t state;
}

View file

@ -1,110 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug - LLVM|x64">
<Configuration>Debug - LLVM</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug - MemLeak|x64">
<Configuration>Debug - MemLeak</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release - LLVM|x64">
<Configuration>Release - LLVM</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|x64'">
<ClCompile>
<TreatWarningAsError>true</TreatWarningAsError>
<AdditionalIncludeDirectories>..\llvm\include;..\llvm_build\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">
<ClCompile>
<TreatWarningAsError>true</TreatWarningAsError>
<AdditionalIncludeDirectories>..\llvm\include;..\llvm_build\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<TreatWarningAsError>true</TreatWarningAsError>
<AdditionalIncludeDirectories>..\llvm\include;..\llvm_build\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<TreatWarningAsError>true</TreatWarningAsError>
<AdditionalIncludeDirectories>..\llvm\include;..\llvm_build\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">
<ClCompile>
<TreatWarningAsError>true</TreatWarningAsError>
<AdditionalIncludeDirectories>..\llvm\include;..\llvm_build\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="Emu\Cell\PPULLVMRecompiler.cpp" />
<ClCompile Include="Emu\Cell\PPULLVMRecompilerCore.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Emu\Cell\PPULLVMRecompiler.h" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="emucore.vcxproj">
<Project>{c4a10229-4712-4bd2-b63e-50d93c67a038}</Project>
</ProjectReference>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{304A6E8B-A311-4EC5-8045-BFA8D08175CE}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>PPULLVMRecompiler</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\rpcs3_default.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="..\rpcs3_debug.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|x64'" Label="PropertySheets">
<Import Project="..\rpcs3_debug.props" />
<Import Project="..\rpcs3_memleak.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'" Label="PropertySheets">
<Import Project="..\rpcs3_debug.props" />
<Import Project="..\rpcs3_llvm.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="..\rpcs3_release.props" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'" Label="PropertySheets">
<Import Project="..\rpcs3_release.props" />
<Import Project="..\rpcs3_llvm.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Emu\Cell\PPULLVMRecompiler.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Emu\Cell\PPULLVMRecompilerCore.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Emu\Cell\PPULLVMRecompiler.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

View file

@ -1,46 +0,0 @@
#include "stdafx.h"
#include "config.h"
namespace rpcs3
{
config_t::config_t(const std::string &path_)
{
path(path_);
}
config_t::config_t(const config_t& rhs)
{
assign(rhs);
}
config_t& config_t::operator =(const config_t& rhs)
{
assign(rhs);
path(rhs.path());
return *this;
}
void config_t::path(const std::string &new_path)
{
m_path = new_path;
}
std::string config_t::path() const
{
return m_path;
}
void config_t::load()
{
fs::file file(m_path, fom::create | fom::read);
if (file)
from_string(file.to_string());
}
void config_t::save() const
{
fs::file(m_path, fom::rewrite).write(to_string());
}
config_t config{ fs::get_config_dir() + "rpcs3.new.ini" };
}

File diff suppressed because it is too large Load diff

View file

@ -1,25 +1,24 @@
#include "stdafx.h"
#include "stdafx_gui.h"
#include "rpcs3.h"
#include "Utilities/Config.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/state.h"
#include "rpcs3.h"
#include "Gui/ConLogFrame.h"
#include "Emu/GameInfo.h"
#include "Emu/Io/Keyboard.h"
#include "Emu/Io/Null/NullKeyboardHandler.h"
#include "Emu/Io/Windows/WindowsKeyboardHandler.h"
#include "BasicKeyboardHandler.h"
#include "Emu/Io/Mouse.h"
#include "Emu/Io/Null/NullMouseHandler.h"
#include "Emu/Io/Windows/WindowsMouseHandler.h"
#include "BasicMouseHandler.h"
#include "Emu/Io/Pad.h"
#include "Emu/Io/Null/NullPadHandler.h"
#include "Emu/Io/Windows/WindowsPadHandler.h"
#include "KeyboardPadHandler.h"
#ifdef _MSC_VER
#include "Emu/Io/XInput/XInputPadHandler.h"
#include "XInputPadHandler.h"
#endif
#include "Emu/RSX/Null/NullGSRender.h"
@ -48,11 +47,72 @@
#include <X11/Xlib.h>
#endif
// GUI config
YAML::Node g_gui_cfg;
// GUI config file
static fs::file s_gui_cfg;
void save_gui_cfg()
{
YAML::Emitter out;
out.SetSeqFormat(YAML::Flow);
out << g_gui_cfg;
// Save to file
s_gui_cfg.seek(0);
s_gui_cfg.trunc(0);
s_gui_cfg.write(out.c_str(), out.size());
}
wxDEFINE_EVENT(wxEVT_DBG_COMMAND, wxCommandEvent);
IMPLEMENT_APP(Rpcs3App)
Rpcs3App* TheApp;
cfg::map_entry<std::function<std::shared_ptr<KeyboardHandlerBase>()>> g_cfg_kb_handler(cfg::root.io, "Keyboard",
{
{ "Null", PURE_EXPR(std::make_shared<NullKeyboardHandler>()) },
{ "Basic", PURE_EXPR(std::make_shared<BasicKeyboardHandler>()) },
});
cfg::map_entry<std::function<std::shared_ptr<MouseHandlerBase>()>> g_cfg_mouse_handler(cfg::root.io, "Mouse",
{
{ "Null", PURE_EXPR(std::make_shared<NullMouseHandler>()) },
{ "Basic", PURE_EXPR(std::make_shared<BasicMouseHandler>()) },
});
cfg::map_entry<std::function<std::shared_ptr<PadHandlerBase>()>> g_cfg_pad_handler(cfg::root.io, "Pad", "Keyboard",
{
{ "Null", PURE_EXPR(std::make_shared<NullPadHandler>()) },
{ "Keyboard", PURE_EXPR(std::make_shared<KeyboardPadHandler>()) },
#ifdef _MSC_VER
{ "XInput", PURE_EXPR(std::make_shared<XInputPadHandler>()) },
#endif
});
cfg::map_entry<std::function<std::shared_ptr<GSRender>()>> g_cfg_gs_render(cfg::root.video, "Renderer", "OpenGL",
{
{ "Null", PURE_EXPR(std::make_shared<NullGSRender>()) },
{ "OpenGL", PURE_EXPR(std::make_shared<GLGSRender>()) },
#ifdef _MSC_VER
{ "DX12", PURE_EXPR(std::make_shared<D3D12GSRender>()) },
{ "Vulkan", PURE_EXPR(std::make_shared<VKGSRender>()) },
#endif
});
cfg::map_entry<std::function<std::shared_ptr<AudioThread>()>> g_cfg_audio_render(cfg::root.audio, "Renderer", "OpenAL",
{
{ "Null", PURE_EXPR(std::make_shared<NullAudioThread>()) },
{ "OpenAL", PURE_EXPR(std::make_shared<OpenALThread>()) },
#ifdef _MSC_VER
{ "XAudio2", PURE_EXPR(std::make_shared<XAudio2Thread>()) },
#endif
});
extern cfg::bool_entry g_cfg_autostart;
extern cfg::bool_entry g_cfg_autoexit;
bool Rpcs3App::OnInit()
{
static const wxCmdLineEntryDesc desc[]
@ -72,6 +132,9 @@ bool Rpcs3App::OnInit()
this->Exit();
}
s_gui_cfg.open(fs::get_config_dir() + "/config_gui.yml", fs::read + fs::write + fs::create);
g_gui_cfg = YAML::Load(s_gui_cfg.to_string());
EmuCallbacks callbacks;
callbacks.call_after = [](std::function<void()> func)
@ -85,83 +148,38 @@ bool Rpcs3App::OnInit()
wxGetApp().ProcessPendingEvents();
};
callbacks.send_dbg_command = [](DbgCommand id, CPUThread* t)
callbacks.exit = [this]()
{
wxGetApp().Exit();
};
callbacks.send_dbg_command = [](DbgCommand id, cpu_thread* t)
{
wxGetApp().SendDbgCommand(id, t);
};
callbacks.get_kb_handler = []() -> std::unique_ptr<KeyboardHandlerBase>
{
switch (auto mode = rpcs3::config.io.keyboard_handler_mode.value())
{
case io_handler_mode::null: return std::make_unique<NullKeyboardHandler>();
case io_handler_mode::windows: return std::make_unique<WindowsKeyboardHandler>();
default: throw EXCEPTION("Invalid Keyboard Handler Mode %d", +(u32)mode);
}
};
callbacks.get_kb_handler = PURE_EXPR(g_cfg_kb_handler.get()());
callbacks.get_mouse_handler = []() -> std::unique_ptr<MouseHandlerBase>
{
switch (auto mode = rpcs3::config.io.mouse_handler_mode.value())
{
case io_handler_mode::null: return std::make_unique<NullMouseHandler>();
case io_handler_mode::windows: return std::make_unique<WindowsMouseHandler>();
default: throw EXCEPTION("Invalid Mouse Handler Mode %d", +(u32)mode);
}
};
callbacks.get_mouse_handler = PURE_EXPR(g_cfg_mouse_handler.get()());
callbacks.get_pad_handler = []() -> std::unique_ptr<PadHandlerBase>
{
switch (auto mode = rpcs3::config.io.pad_handler_mode.value())
{
case io_handler_mode::null: return std::make_unique<NullPadHandler>();
case io_handler_mode::windows: return std::make_unique<WindowsPadHandler>();
#ifdef _MSC_VER
case io_handler_mode::xinput: return std::make_unique<XInputPadHandler>();
#endif
default: throw EXCEPTION("Invalid Pad Handler Mode %d", (int)mode);
}
};
callbacks.get_pad_handler = PURE_EXPR(g_cfg_pad_handler.get()());
callbacks.get_gs_frame = [](frame_type type) -> std::unique_ptr<GSFrameBase>
callbacks.get_gs_frame = [](frame_type type, size2i size) -> std::unique_ptr<GSFrameBase>
{
switch (type)
{
case frame_type::OpenGL: return std::make_unique<GLGSFrame>();
case frame_type::DX12: return std::make_unique<GSFrame>("DirectX 12");
case frame_type::Null: return std::make_unique<GSFrame>("Null");
case frame_type::Vulkan: return std::make_unique<GSFrame>("Vulkan");
case frame_type::OpenGL: return std::make_unique<GLGSFrame>(size);
case frame_type::DX12: return std::make_unique<GSFrame>("DirectX 12", size);
case frame_type::Null: return std::make_unique<GSFrame>("Null", size);
case frame_type::Vulkan: return std::make_unique<GSFrame>("Vulkan", size);
}
throw EXCEPTION("Invalid Frame Type");
throw EXCEPTION("Invalid Frame Type (0x%x)", type);
};
callbacks.get_gs_render = []() -> std::shared_ptr<GSRender>
{
switch (auto mode = rpcs3::state.config.rsx.renderer.value())
{
case rsx_renderer_type::Null: return std::make_shared<NullGSRender>();
case rsx_renderer_type::OpenGL: return std::make_shared<GLGSRender>();
#ifdef _MSC_VER
case rsx_renderer_type::DX12: return std::make_shared<D3D12GSRender>();
case rsx_renderer_type::Vulkan: return std::make_shared<VKGSRender>();
#endif
default: throw EXCEPTION("Invalid GS Renderer %d", (int)mode);
}
};
callbacks.get_gs_render = PURE_EXPR(g_cfg_gs_render.get()());
callbacks.get_audio = []() -> std::shared_ptr<AudioThread>
{
switch (rpcs3::state.config.audio.out.value())
{
default:
case audio_output_type::Null: return std::make_shared<NullAudioThread>();
case audio_output_type::OpenAL: return std::make_shared<OpenALThread>();
#ifdef _MSC_VER
case audio_output_type::XAudio2: return std::make_shared<XAudio2Thread>();
#endif
}
};
callbacks.get_audio = PURE_EXPR(g_cfg_audio_render.get()());
callbacks.get_msg_dialog = []() -> std::shared_ptr<MsgDialogBase>
{
@ -199,13 +217,15 @@ void Rpcs3App::OnArguments(const wxCmdLineParser& parser)
if (parser.FoundSwitch("t"))
{
HLEExitOnStop = rpcs3::config.misc.exit_on_stop.value();
rpcs3::config.misc.exit_on_stop = true;
if (parser.GetParamCount() != 1)
{
wxLogDebug(wxT("A (S)ELF file needs to be given in test mode, exiting."));
wxLogDebug("A (S)ELF file needs to be given in test mode, exiting.");
this->Exit();
}
// TODO: clean implementation
g_cfg_autostart = true;
g_cfg_autoexit = true;
}
if (parser.GetParamCount() > 0)
@ -218,16 +238,11 @@ void Rpcs3App::OnArguments(const wxCmdLineParser& parser)
void Rpcs3App::Exit()
{
if (parser.FoundSwitch("t"))
{
rpcs3::config.misc.exit_on_stop = HLEExitOnStop;
}
Emu.Stop();
wxApp::Exit();
}
void Rpcs3App::SendDbgCommand(DbgCommand id, CPUThread* thr)
void Rpcs3App::SendDbgCommand(DbgCommand id, cpu_thread* thr)
{
wxCommandEvent event(wxEVT_DBG_COMMAND, id);
event.SetClientData(thr);

View file

@ -1,21 +1,17 @@
#pragma once
#include "Gui/MainFrame.h"
#include "Emu/DbgCommand.h"
#include "Utilities/Thread.h"
#include <wx/app.h>
#include <wx/cmdline.h>
class CPUThread;
wxDECLARE_EVENT(wxEVT_DBG_COMMAND, wxCommandEvent);
class Rpcs3App : public wxApp
{
private:
wxCmdLineParser parser;
// Used to restore the configuration state after a test run
bool HLEExitOnStop;
public:
MainFrame* m_MainFrame;
@ -25,12 +21,9 @@ public:
Rpcs3App();
void SendDbgCommand(DbgCommand id, CPUThread* thr=nullptr);
void SendDbgCommand(DbgCommand id, class cpu_thread* thr = nullptr);
};
DECLARE_APP(Rpcs3App)
//extern CPUThread& GetCPU(const u8 core);
extern Rpcs3App* TheApp;
static const u64 PS3_CLK = 3200000000;